1.0.0
This commit is contained in:
@@ -7,7 +7,6 @@ import { eq } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's first organization
|
||||
const userMembership = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -15,7 +14,6 @@ const userMembership = await db.select()
|
||||
|
||||
if (!userMembership) return Astro.redirect('/dashboard');
|
||||
|
||||
// Get all categories for the organization
|
||||
const allCategories = await db.select()
|
||||
.from(categories)
|
||||
.where(eq(categories.organizationId, userMembership.organizationId))
|
||||
|
||||
@@ -7,7 +7,6 @@ import { eq, and } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's organizations
|
||||
const userOrgs = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -15,7 +14,6 @@ const userOrgs = await db.select()
|
||||
|
||||
const orgIds = userOrgs.map(m => m.organizationId);
|
||||
|
||||
// Get all clients for user's organizations
|
||||
const allClients = orgIds.length > 0
|
||||
? await db.select()
|
||||
.from(clients)
|
||||
|
||||
@@ -19,7 +19,6 @@ const userOrgs = await db.select({
|
||||
.where(eq(members.userId, user.id))
|
||||
.all();
|
||||
|
||||
// Get stats for first organization
|
||||
const firstOrg = userOrgs[0];
|
||||
let stats = {
|
||||
totalTimeThisWeek: 0,
|
||||
@@ -30,12 +29,10 @@ let stats = {
|
||||
};
|
||||
|
||||
if (firstOrg) {
|
||||
// Calculate date ranges
|
||||
const now = new Date();
|
||||
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
|
||||
// Get time entries for this week
|
||||
const weekEntries = await db.select()
|
||||
.from(timeEntries)
|
||||
.where(and(
|
||||
@@ -51,7 +48,6 @@ if (firstOrg) {
|
||||
return sum;
|
||||
}, 0);
|
||||
|
||||
// Get time entries for this month
|
||||
const monthEntries = await db.select()
|
||||
.from(timeEntries)
|
||||
.where(and(
|
||||
@@ -67,7 +63,6 @@ if (firstOrg) {
|
||||
return sum;
|
||||
}, 0);
|
||||
|
||||
// Count active timers
|
||||
const activeCount = await db.select()
|
||||
.from(timeEntries)
|
||||
.where(and(
|
||||
@@ -78,7 +73,6 @@ if (firstOrg) {
|
||||
|
||||
stats.activeTimers = activeCount.length;
|
||||
|
||||
// Count clients
|
||||
const clientCount = await db.select()
|
||||
.from(clients)
|
||||
.where(eq(clients.organizationId, firstOrg.organizationId))
|
||||
@@ -86,7 +80,6 @@ if (firstOrg) {
|
||||
|
||||
stats.totalClients = clientCount.length;
|
||||
|
||||
// Get recent entries
|
||||
stats.recentEntries = await db.select({
|
||||
entry: timeEntries,
|
||||
client: clients,
|
||||
|
||||
@@ -11,7 +11,6 @@ import { eq, and, gte, lte, sql, desc } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's organization
|
||||
const userMembership = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -19,7 +18,6 @@ const userMembership = await db.select()
|
||||
|
||||
if (!userMembership) return Astro.redirect('/dashboard');
|
||||
|
||||
// Get all team members
|
||||
const teamMembers = await db.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
@@ -30,26 +28,22 @@ const teamMembers = await db.select({
|
||||
.where(eq(members.organizationId, userMembership.organizationId))
|
||||
.all();
|
||||
|
||||
// Get all categories
|
||||
const allCategories = await db.select()
|
||||
.from(categories)
|
||||
.where(eq(categories.organizationId, userMembership.organizationId))
|
||||
.all();
|
||||
|
||||
// Get all clients
|
||||
const allClients = await db.select()
|
||||
.from(clients)
|
||||
.where(eq(clients.organizationId, userMembership.organizationId))
|
||||
.all();
|
||||
|
||||
// Parse filter parameters
|
||||
const url = new URL(Astro.request.url);
|
||||
const selectedMemberId = url.searchParams.get('member') || '';
|
||||
const selectedCategoryId = url.searchParams.get('category') || '';
|
||||
const selectedClientId = url.searchParams.get('client') || '';
|
||||
const timeRange = url.searchParams.get('range') || 'week';
|
||||
|
||||
// Calculate date range
|
||||
const now = new Date();
|
||||
let startDate = new Date();
|
||||
let endDate = new Date();
|
||||
@@ -77,7 +71,6 @@ switch (timeRange) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Build query conditions
|
||||
const conditions = [
|
||||
eq(timeEntries.organizationId, userMembership.organizationId),
|
||||
gte(timeEntries.startTime, startDate),
|
||||
@@ -96,7 +89,6 @@ if (selectedClientId) {
|
||||
conditions.push(eq(timeEntries.clientId, selectedClientId));
|
||||
}
|
||||
|
||||
// Fetch detailed entries
|
||||
const entries = await db.select({
|
||||
entry: timeEntries,
|
||||
user: users,
|
||||
@@ -111,7 +103,6 @@ const entries = await db.select({
|
||||
.orderBy(desc(timeEntries.startTime))
|
||||
.all();
|
||||
|
||||
// Calculate statistics by member
|
||||
const statsByMember = teamMembers.map(member => {
|
||||
const memberEntries = entries.filter(e => e.user.id === member.id);
|
||||
const totalTime = memberEntries.reduce((sum, e) => {
|
||||
@@ -128,7 +119,6 @@ const statsByMember = teamMembers.map(member => {
|
||||
};
|
||||
}).sort((a, b) => b.totalTime - a.totalTime);
|
||||
|
||||
// Calculate statistics by category
|
||||
const statsByCategory = allCategories.map(category => {
|
||||
const categoryEntries = entries.filter(e => e.category.id === category.id);
|
||||
const totalTime = categoryEntries.reduce((sum, e) => {
|
||||
@@ -145,7 +135,6 @@ const statsByCategory = allCategories.map(category => {
|
||||
};
|
||||
}).sort((a, b) => b.totalTime - a.totalTime);
|
||||
|
||||
// Calculate statistics by client
|
||||
const statsByClient = allClients.map(client => {
|
||||
const clientEntries = entries.filter(e => e.client.id === client.id);
|
||||
const totalTime = clientEntries.reduce((sum, e) => {
|
||||
@@ -162,7 +151,6 @@ const statsByClient = allClients.map(client => {
|
||||
};
|
||||
}).sort((a, b) => b.totalTime - a.totalTime);
|
||||
|
||||
// Calculate total time
|
||||
const totalTime = entries.reduce((sum, e) => {
|
||||
if (e.entry.endTime) {
|
||||
return sum + (e.entry.endTime.getTime() - e.entry.startTime.getTime());
|
||||
|
||||
@@ -8,7 +8,6 @@ import { eq } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's first organization
|
||||
const userMembership = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -16,7 +15,6 @@ const userMembership = await db.select()
|
||||
|
||||
if (!userMembership) return Astro.redirect('/dashboard');
|
||||
|
||||
// Get all team members for this organization
|
||||
const teamMembers = await db.select({
|
||||
member: members,
|
||||
user: users,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { eq } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's membership to check if they're admin
|
||||
const userMembership = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
|
||||
@@ -8,7 +8,6 @@ import { eq } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's membership to check if they're admin
|
||||
const userMembership = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -19,7 +18,6 @@ if (!userMembership) return Astro.redirect('/dashboard');
|
||||
const isAdmin = userMembership.role === 'owner' || userMembership.role === 'admin';
|
||||
if (!isAdmin) return Astro.redirect('/dashboard/team');
|
||||
|
||||
// Get all categories for the organization
|
||||
const allCategories = await db.select()
|
||||
.from(categories)
|
||||
.where(eq(categories.organizationId, userMembership.organizationId))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
import DashboardLayout from '../../../../../layouts/DashboardLayout.astro';
|
||||
import DashboardLayout from '../../../../../../layouts/DashboardLayout.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { db } from '../../../../../db';
|
||||
import { categories, members } from '../../../../../db/schema';
|
||||
import { db } from '../../../../../../db';
|
||||
import { categories, members } from '../../../../../../db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
|
||||
const user = Astro.locals.user;
|
||||
@@ -10,7 +10,6 @@ if (!user) return Astro.redirect('/login');
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
||||
// Get user's membership
|
||||
const userMembership = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -21,7 +20,6 @@ if (!userMembership) return Astro.redirect('/dashboard');
|
||||
const isAdmin = userMembership.role === 'owner' || userMembership.role === 'admin';
|
||||
if (!isAdmin) return Astro.redirect('/dashboard/team/settings');
|
||||
|
||||
// Get category
|
||||
const category = await db.select()
|
||||
.from(categories)
|
||||
.where(and(
|
||||
|
||||
@@ -9,7 +9,6 @@ import { eq, desc, asc, and, sql, or, like } from 'drizzle-orm';
|
||||
const user = Astro.locals.user;
|
||||
if (!user) return Astro.redirect('/login');
|
||||
|
||||
// Get user's first organization
|
||||
const userOrg = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
@@ -17,25 +16,22 @@ const userOrg = await db.select()
|
||||
|
||||
if (!userOrg) return Astro.redirect('/dashboard');
|
||||
|
||||
// Get all clients for the organization
|
||||
const allClients = await db.select()
|
||||
.from(clients)
|
||||
.where(eq(clients.organizationId, userOrg.organizationId))
|
||||
.all();
|
||||
|
||||
// Get all categories for the organization
|
||||
const allCategories = await db.select()
|
||||
.from(categories)
|
||||
.where(eq(categories.organizationId, userOrg.organizationId))
|
||||
.all();
|
||||
|
||||
// Get all tags for the organization
|
||||
const allTags = await db.select()
|
||||
.from(tags)
|
||||
.where(eq(tags.organizationId, userOrg.organizationId))
|
||||
.all();
|
||||
|
||||
// Parse query parameters for filtering, sorting, and pagination
|
||||
// Query params
|
||||
const url = new URL(Astro.request.url);
|
||||
const page = parseInt(url.searchParams.get('page') || '1');
|
||||
const pageSize = 20;
|
||||
@@ -43,11 +39,10 @@ const offset = (page - 1) * pageSize;
|
||||
|
||||
const filterClient = url.searchParams.get('client') || '';
|
||||
const filterCategory = url.searchParams.get('category') || '';
|
||||
const filterStatus = url.searchParams.get('status') || ''; // completed, running, all
|
||||
const sortBy = url.searchParams.get('sort') || 'start-desc'; // start-desc, start-asc, duration-desc, duration-asc
|
||||
const filterStatus = url.searchParams.get('status') || '';
|
||||
const sortBy = url.searchParams.get('sort') || 'start-desc';
|
||||
const searchTerm = url.searchParams.get('search') || '';
|
||||
|
||||
// Build query conditions
|
||||
const conditions = [eq(timeEntries.organizationId, userOrg.organizationId)];
|
||||
|
||||
if (filterClient) {
|
||||
@@ -68,7 +63,6 @@ if (searchTerm) {
|
||||
conditions.push(like(timeEntries.description, `%${searchTerm}%`));
|
||||
}
|
||||
|
||||
// Get total count for pagination
|
||||
const totalCount = await db.select({ count: sql<number>`count(*)` })
|
||||
.from(timeEntries)
|
||||
.where(and(...conditions))
|
||||
@@ -76,7 +70,6 @@ const totalCount = await db.select({ count: sql<number>`count(*)` })
|
||||
|
||||
const totalPages = Math.ceil((totalCount?.count || 0) / pageSize);
|
||||
|
||||
// Build order by
|
||||
let orderBy;
|
||||
switch (sortBy) {
|
||||
case 'start-asc':
|
||||
@@ -88,7 +81,7 @@ switch (sortBy) {
|
||||
case 'duration-asc':
|
||||
orderBy = asc(sql`(CASE WHEN ${timeEntries.endTime} IS NULL THEN 0 ELSE ${timeEntries.endTime} - ${timeEntries.startTime} END)`);
|
||||
break;
|
||||
default: // start-desc
|
||||
default:
|
||||
orderBy = desc(timeEntries.startTime);
|
||||
}
|
||||
|
||||
@@ -129,7 +122,6 @@ function formatDuration(start: Date, end: Date | null) {
|
||||
return `${hours}h ${mins}m`;
|
||||
}
|
||||
|
||||
// Generate pagination page numbers
|
||||
function getPaginationPages(currentPage: number, totalPages: number): number[] {
|
||||
const pages: number[] = [];
|
||||
const numPagesToShow = Math.min(5, totalPages);
|
||||
|
||||
Reference in New Issue
Block a user