Icon refactor

This commit is contained in:
2026-02-12 14:29:12 -07:00
parent caf763aa1e
commit 1c70626f5a
36 changed files with 329 additions and 607 deletions

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../../components/Icon.astro';
import { db } from '../../../../db';
import { clients } from '../../../../db/schema';
import { eq, and } from 'drizzle-orm';
@@ -30,7 +30,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-3 mb-6">
<a href={`/dashboard/clients/${client.id}`} class="btn btn-ghost btn-xs">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
</a>
<h1 class="text-2xl font-extrabold tracking-tight">Edit Client</h1>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../../components/Icon.astro';
import { db } from '../../../../db';
import { clients, timeEntries, tags, users } from '../../../../db/schema';
import { eq, and, desc, sql } from 'drizzle-orm';
@@ -64,7 +64,7 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
<DashboardLayout title={`${client.name} - Clients - Chronus`}>
<div class="flex items-center gap-3 mb-6">
<a href="/dashboard/clients" class="btn btn-ghost btn-xs">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
</a>
<h1 class="text-2xl font-extrabold tracking-tight">{client.name}</h1>
</div>
@@ -79,19 +79,19 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
<div class="space-y-2 mb-4">
{client.email && (
<div class="flex items-center gap-2 text-base-content/60 text-sm">
<Icon name="heroicons:envelope" class="w-4 h-4" />
<Icon name="envelope" class="w-4 h-4" />
<a href={`mailto:${client.email}`} class="link link-hover">{client.email}</a>
</div>
)}
{client.phone && (
<div class="flex items-center gap-2 text-base-content/60 text-sm">
<Icon name="heroicons:phone" class="w-4 h-4" />
<Icon name="phone" class="w-4 h-4" />
<a href={`tel:${client.phone}`} class="link link-hover">{client.phone}</a>
</div>
)}
{(client.street || client.city || client.state || client.zip || client.country) && (
<div class="flex items-start gap-2 text-base-content/60">
<Icon name="heroicons:map-pin" class="w-4 h-4 mt-0.5" />
<Icon name="map-pin" class="w-4 h-4 mt-0.5" />
<div class="text-sm space-y-0.5">
{client.street && <div>{client.street}</div>}
{(client.city || client.state || client.zip) && (
@@ -107,12 +107,12 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
</div>
<div class="flex gap-2">
<a href={`/dashboard/clients/${client.id}/edit`} class="btn btn-primary btn-xs">
<Icon name="heroicons:pencil" class="w-3 h-3" />
<Icon name="pencil" class="w-3 h-3" />
Edit
</a>
<form method="POST" action={`/api/clients/${client.id}/delete`} onsubmit="return confirm('Are you sure you want to delete this client? This will also delete all associated time entries.');">
<button type="submit" class="btn btn-error btn-outline btn-xs">
<Icon name="heroicons:trash" class="w-3 h-3" />
<Icon name="trash" class="w-3 h-3" />
Delete
</button>
</form>
@@ -126,14 +126,14 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
title="Total Time Tracked"
value={`${totalHours}h ${totalMinutes}m`}
description="Across all projects"
icon="heroicons:clock"
icon="clock"
color="text-primary"
/>
<StatCard
title="Total Entries"
value={String(totalEntriesCount)}
description="Recorded entries"
icon="heroicons:list-bullet"
icon="list-bullet"
color="text-secondary"
/>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../components/Icon.astro';
import StatCard from '../../components/StatCard.astro';
import { db } from '../../db';
import { organizations, members, timeEntries, clients, tags } from '../../db/schema';
@@ -112,20 +112,20 @@ const hasMembership = userOrgs.length > 0;
<p class="text-base-content/60 text-sm mt-1">Welcome back, {user.name}!</p>
</div>
<a href="/dashboard/organizations/new" class="btn btn-ghost btn-sm">
<Icon name="heroicons:plus" class="w-4 h-4" />
<Icon name="plus" class="w-4 h-4" />
New Team
</a>
</div>
{!hasMembership && (
<div class="alert alert-info mb-6 text-sm">
<Icon name="heroicons:information-circle" class="w-5 h-5" />
<Icon name="information-circle" class="w-5 h-5" />
<div>
<h3 class="font-bold">Welcome to Chronus!</h3>
<div class="text-xs">You're not part of any team yet. Create one or wait for an invitation.</div>
</div>
<a href="/dashboard/organizations/new" class="btn btn-primary btn-sm">
<Icon name="heroicons:plus" class="w-4 h-4" />
<Icon name="plus" class="w-4 h-4" />
New Team
</a>
</div>
@@ -139,28 +139,28 @@ const hasMembership = userOrgs.length > 0;
title="This Week"
value={formatDuration(stats.totalTimeThisWeek)}
description="Total tracked time"
icon="heroicons:clock"
icon="clock"
color="text-primary"
/>
<StatCard
title="This Month"
value={formatDuration(stats.totalTimeThisMonth)}
description="Total tracked time"
icon="heroicons:calendar"
icon="calendar"
color="text-secondary"
/>
<StatCard
title="Active Timers"
value={String(stats.activeTimers)}
description="Currently running"
icon="heroicons:play-circle"
icon="play-circle"
color="text-accent"
/>
<StatCard
title="Clients"
value={String(stats.totalClients)}
description="Total active"
icon="heroicons:building-office"
icon="building-office"
color="text-info"
/>
</div>
@@ -170,20 +170,20 @@ const hasMembership = userOrgs.length > 0;
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2">
<Icon name="heroicons:bolt" class="w-4 h-4 text-warning" />
<Icon name="bolt" class="w-4 h-4 text-warning" />
Quick Actions
</h2>
<div class="flex flex-col gap-2 mt-3">
<a href="/dashboard/tracker" class="btn btn-primary btn-sm">
<Icon name="heroicons:play" class="w-4 h-4" />
<Icon name="play" class="w-4 h-4" />
Start Timer
</a>
<a href="/dashboard/clients/new" class="btn btn-ghost btn-sm">
<Icon name="heroicons:plus" class="w-4 h-4" />
<Icon name="plus" class="w-4 h-4" />
Add Client
</a>
<a href="/dashboard/reports" class="btn btn-ghost btn-sm">
<Icon name="heroicons:chart-bar" class="w-4 h-4" />
<Icon name="chart-bar" class="w-4 h-4" />
View Reports
</a>
</div>
@@ -194,7 +194,7 @@ const hasMembership = userOrgs.length > 0;
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2">
<Icon name="heroicons:clock" class="w-4 h-4 text-success" />
<Icon name="clock" class="w-4 h-4 text-success" />
Recent Activity
</h2>
{stats.recentEntries.length > 0 ? (
@@ -215,7 +215,7 @@ const hasMembership = userOrgs.length > 0;
</ul>
) : (
<div class="flex flex-col items-center justify-center py-6 text-center mt-3">
<Icon name="heroicons:clock" class="w-10 h-10 text-base-content/15 mb-2" />
<Icon name="clock" class="w-10 h-10 text-base-content/15 mb-2" />
<p class="text-base-content/40 text-sm">No recent time entries</p>
</div>
)}

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../components/Icon.astro';
import { db } from '../../../db';
import { invoices, invoiceItems, clients, members, organizations } from '../../../db/schema';
import { eq, and } from 'drizzle-orm';
@@ -60,7 +60,7 @@ const isDraft = invoice.status === 'draft';
<div>
<div class="flex items-center gap-2 mb-1">
<a href="/dashboard/invoices" class="btn btn-ghost btn-xs btn-square">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
</a>
<div class={`badge badge-xs ${
invoice.status === 'paid' || invoice.status === 'accepted' ? 'badge-success' :
@@ -79,7 +79,7 @@ const isDraft = invoice.status === 'draft';
<form method="POST" action={`/api/invoices/${invoice.id}/status`}>
<input type="hidden" name="status" value="sent" />
<button type="submit" class="btn btn-primary btn-sm">
<Icon name="heroicons:paper-airplane" class="w-4 h-4" />
<Icon name="paper-airplane" class="w-4 h-4" />
Mark Sent
</button>
</form>
@@ -88,7 +88,7 @@ const isDraft = invoice.status === 'draft';
<form method="POST" action={`/api/invoices/${invoice.id}/status`}>
<input type="hidden" name="status" value="paid" />
<button type="submit" class="btn btn-success btn-sm">
<Icon name="heroicons:check" class="w-4 h-4" />
<Icon name="check" class="w-4 h-4" />
Mark Paid
</button>
</form>
@@ -97,7 +97,7 @@ const isDraft = invoice.status === 'draft';
<form method="POST" action={`/api/invoices/${invoice.id}/status`}>
<input type="hidden" name="status" value="accepted" />
<button type="submit" class="btn btn-success btn-sm">
<Icon name="heroicons:check" class="w-4 h-4" />
<Icon name="check" class="w-4 h-4" />
Mark Accepted
</button>
</form>
@@ -105,25 +105,25 @@ const isDraft = invoice.status === 'draft';
{(invoice.type === 'quote' && invoice.status === 'accepted') && (
<form method="POST" action={`/api/invoices/${invoice.id}/convert`}>
<button type="submit" class="btn btn-primary btn-sm">
<Icon name="heroicons:document-duplicate" class="w-4 h-4" />
<Icon name="document-duplicate" class="w-4 h-4" />
Convert to Invoice
</button>
</form>
)}
<div class="dropdown dropdown-end">
<div role="button" tabindex="0" class="btn btn-square btn-ghost btn-sm border border-base-200">
<Icon name="heroicons:ellipsis-horizontal" class="w-4 h-4" />
<Icon name="ellipsis-horizontal" class="w-4 h-4" />
</div>
<ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-200">
<li>
<a href={`/dashboard/invoices/${invoice.id}/edit`}>
<Icon name="heroicons:pencil-square" class="w-4 h-4" />
<Icon name="pencil-square" class="w-4 h-4" />
Edit Settings
</a>
</li>
<li>
<a href={`/api/invoices/${invoice.id}/generate`} download>
<Icon name="heroicons:arrow-down-tray" class="w-4 h-4" />
<Icon name="arrow-down-tray" class="w-4 h-4" />
Download PDF
</a>
</li>
@@ -132,7 +132,7 @@ const isDraft = invoice.status === 'draft';
<form method="POST" action={`/api/invoices/${invoice.id}/status`}>
<input type="hidden" name="status" value="void" />
<button type="submit" class="text-error">
<Icon name="heroicons:x-circle" class="w-4 h-4" />
<Icon name="x-circle" class="w-4 h-4" />
Void
</button>
</form>
@@ -142,7 +142,7 @@ const isDraft = invoice.status === 'draft';
<form method="POST" action="/api/invoices/delete" onsubmit="return confirm('Are you sure?');">
<input type="hidden" name="id" value={invoice.id} />
<button type="submit" class="text-error">
<Icon name="heroicons:trash" class="w-4 h-4" />
<Icon name="trash" class="w-4 h-4" />
Delete
</button>
</form>
@@ -236,7 +236,7 @@ const isDraft = invoice.status === 'draft';
<form method="POST" action={`/api/invoices/${invoice.id}/items/delete`}>
<input type="hidden" name="itemId" value={item.id} />
<button type="submit" class="btn btn-ghost btn-xs btn-square text-error opacity-50 hover:opacity-100">
<Icon name="heroicons:trash" class="w-4 h-4" />
<Icon name="trash" class="w-4 h-4" />
</button>
</form>
</td>
@@ -259,7 +259,7 @@ const isDraft = invoice.status === 'draft';
{isDraft && (
<div class="flex justify-end mb-4">
<button onclick="document.getElementById('import_time_modal').showModal()" class="btn btn-sm btn-outline gap-2">
<Icon name="heroicons:clock" class="w-4 h-4" />
<Icon name="clock" class="w-4 h-4" />
Import Time
</button>
</div>
@@ -281,7 +281,7 @@ const isDraft = invoice.status === 'draft';
</div>
<div class="sm:col-span-1">
<button type="submit" class="btn btn-sm btn-primary w-full">
<Icon name="heroicons:plus" class="w-4 h-4" />
<Icon name="plus" class="w-4 h-4" />
</button>
</div>
</div>
@@ -310,7 +310,7 @@ const isDraft = invoice.status === 'draft';
Tax ({invoice.taxRate ?? 0}%)
{isDraft && (
<button type="button" onclick="document.getElementById('tax_modal').showModal()" class="btn btn-ghost btn-xs btn-square opacity-0 group-hover:opacity-100 transition-opacity" title="Edit Tax Rate">
<Icon name="heroicons:pencil" class="w-3 h-3" />
<Icon name="pencil" class="w-3 h-3" />
</button>
)}
</span>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../../components/Icon.astro';
import { db } from '../../../../db';
import { invoices, members } from '../../../../db/schema';
import { eq, and } from 'drizzle-orm';
@@ -48,7 +48,7 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<div class="max-w-3xl mx-auto">
<div class="mb-6">
<a href={`/dashboard/invoices/${invoice.id}`} class="btn btn-ghost btn-xs gap-2 pl-0 hover:bg-transparent text-base-content/60">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
Back to Invoice
</a>
<h1 class="text-2xl font-extrabold tracking-tight mt-2">Edit Details</h1>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../components/Icon.astro';
import StatCard from '../../../components/StatCard.astro';
import { db } from '../../../db';
import { invoices, clients } from '../../../db/schema';
@@ -108,7 +108,7 @@ const getStatusColor = (status: string) => {
<p class="text-base-content/60 text-sm mt-1">Manage your billing and estimates</p>
</div>
<a href="/dashboard/invoices/new" class="btn btn-primary btn-sm">
<Icon name="heroicons:plus" class="w-4 h-4" />
<Icon name="plus" class="w-4 h-4" />
Create New
</a>
</div>
@@ -118,14 +118,14 @@ const getStatusColor = (status: string) => {
title="Total Invoices"
value={String(yearInvoices.filter(i => i.invoice.type === 'invoice').length)}
description={selectedYear === 'current' ? `${currentYear} (YTD)` : String(selectedYear)}
icon="heroicons:document-text"
icon="document-text"
color="text-primary"
/>
<StatCard
title="Open Quotes"
value={String(yearInvoices.filter(i => i.invoice.type === 'quote' && i.invoice.status === 'sent').length)}
description="Waiting for approval"
icon="heroicons:clipboard-document-list"
icon="clipboard-document-list"
color="text-secondary"
/>
<StatCard
@@ -134,7 +134,7 @@ const getStatusColor = (status: string) => {
.filter(i => i.invoice.type === 'invoice' && i.invoice.status === 'paid')
.reduce((acc, curr) => acc + curr.invoice.total, 0), 'USD')}
description={`Paid invoices (${selectedYear === 'current' ? `${currentYear} YTD` : selectedYear})`}
icon="heroicons:currency-dollar"
icon="currency-dollar"
color="text-success"
/>
</div>
@@ -191,7 +191,7 @@ const getStatusColor = (status: string) => {
{(selectedYear !== 'current' || selectedType !== 'all' || selectedStatus !== 'all' || sortBy !== 'date-desc') && (
<div class="mt-3">
<a href="/dashboard/invoices" class="btn btn-ghost btn-xs">
<Icon name="heroicons:x-mark" class="w-3 h-3" />
<Icon name="x-mark" class="w-3 h-3" />
Clear Filters
</a>
</div>
@@ -260,24 +260,24 @@ const getStatusColor = (status: string) => {
<td class="text-right">
<div class="dropdown dropdown-end">
<div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square">
<Icon name="heroicons:ellipsis-vertical" class="w-4 h-4" />
<Icon name="ellipsis-vertical" class="w-4 h-4" />
</div>
<ul tabindex="0" class="dropdown-content menu p-2 bg-base-100 rounded-box w-52 border border-base-200 z-100">
<li>
<a href={`/dashboard/invoices/${invoice.id}`}>
<Icon name="heroicons:eye" class="w-4 h-4" />
<Icon name="eye" class="w-4 h-4" />
View Details
</a>
</li>
<li>
<a href={`/dashboard/invoices/${invoice.id}/edit`}>
<Icon name="heroicons:pencil-square" class="w-4 h-4" />
<Icon name="pencil-square" class="w-4 h-4" />
Edit
</a>
</li>
<li>
<a href={`/api/invoices/${invoice.id}/generate`} download>
<Icon name="heroicons:arrow-down-tray" class="w-4 h-4" />
<Icon name="arrow-down-tray" class="w-4 h-4" />
Download PDF
</a>
</li>
@@ -286,7 +286,7 @@ const getStatusColor = (status: string) => {
<form method="POST" action={`/api/invoices/${invoice.id}/status`} class="w-full">
<input type="hidden" name="status" value="sent" />
<button type="submit" class="w-full justify-start">
<Icon name="heroicons:paper-airplane" class="w-4 h-4" />
<Icon name="paper-airplane" class="w-4 h-4" />
Mark as Sent
</button>
</form>
@@ -297,7 +297,7 @@ const getStatusColor = (status: string) => {
<form method="POST" action={`/api/invoices/delete`} onsubmit="return confirm('Are you sure? This action cannot be undone.');" class="w-full">
<input type="hidden" name="id" value={invoice.id} />
<button type="submit" class="w-full justify-start text-error hover:bg-error/10">
<Icon name="heroicons:trash" class="w-4 h-4" />
<Icon name="trash" class="w-4 h-4" />
Delete
</button>
</form>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../components/Icon.astro';
import { db } from '../../../db';
import { clients, invoices, organizations } from '../../../db/schema';
import { eq, desc, and } from 'drizzle-orm';
@@ -81,7 +81,7 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<div class="max-w-3xl mx-auto">
<div class="mb-6">
<a href="/dashboard/invoices" class="btn btn-ghost btn-xs gap-2 pl-0 hover:bg-transparent text-base-content/60">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
Back to Invoices
</a>
<h1 class="text-2xl font-extrabold tracking-tight mt-2">Create New Document</h1>
@@ -89,7 +89,7 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
{teamClients.length === 0 ? (
<div role="alert" class="alert alert-warning">
<Icon name="heroicons:exclamation-triangle" class="w-5 h-5" />
<Icon name="exclamation-triangle" class="w-5 h-5" />
<div>
<h3 class="font-semibold text-sm">No Clients Found</h3>
<div class="text-xs">You need to add a client before you can create an invoice or quote.</div>
@@ -187,7 +187,7 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<a href="/dashboard/invoices" class="btn btn-ghost btn-sm">Cancel</a>
<button type="submit" class="btn btn-primary btn-sm">
Create Draft
<Icon name="heroicons:arrow-right" class="w-4 h-4" />
<Icon name="arrow-right" class="w-4 h-4" />
</button>
</div>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../components/Icon.astro';
import { db } from '../../../db';
import { organizations, members } from '../../../db/schema';
import { eq } from 'drizzle-orm';
@@ -13,7 +13,7 @@ if (!user) return Astro.redirect('/login');
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-3 mb-6">
<a href="/dashboard" class="btn btn-ghost btn-xs">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
</a>
<h1 class="text-2xl font-extrabold tracking-tight">Create New Team</h1>
</div>
@@ -21,7 +21,7 @@ if (!user) return Astro.redirect('/login');
<form method="POST" action="/api/organizations/create" class="card card-border bg-base-100">
<div class="card-body p-4">
<div class="alert alert-info mb-4">
<Icon name="heroicons:information-circle" class="w-4 h-4" />
<Icon name="information-circle" class="w-4 h-4" />
<span class="text-sm">Create a new team to manage separate projects and collaborators. You'll be the owner.</span>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../components/Icon.astro';
import StatCard from '../../components/StatCard.astro';
import TagChart from '../../components/TagChart.vue';
import ClientChart from '../../components/ClientChart.vue';
@@ -343,28 +343,28 @@ function getTimeRangeLabel(range: string) {
title="Total Time"
value={formatDuration(totalTime)}
description={getTimeRangeLabel(timeRange)}
icon="heroicons:clock"
icon="clock"
color="text-primary"
/>
<StatCard
title="Total Entries"
value={String(entries.length)}
description={getTimeRangeLabel(timeRange)}
icon="heroicons:list-bullet"
icon="list-bullet"
color="text-secondary"
/>
<StatCard
title="Revenue"
value={formatCurrency(revenueStats.total)}
description={`${invoiceStats.paid} paid invoices`}
icon="heroicons:currency-dollar"
icon="currency-dollar"
color="text-success"
/>
<StatCard
title="Active Members"
value={String(statsByMember.filter(s => s.entryCount > 0).length)}
description={`of ${teamMembers.length} total`}
icon="heroicons:user-group"
icon="user-group"
color="text-accent"
/>
</div>
@@ -374,7 +374,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:document-text" class="w-4 h-4" />
<Icon name="document-text" class="w-4 h-4" />
Invoices Overview
</h2>
<div class="grid grid-cols-2 gap-4">
@@ -410,7 +410,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:clipboard-document-list" class="w-4 h-4" />
<Icon name="clipboard-document-list" class="w-4 h-4" />
Quotes Overview
</h2>
<div class="grid grid-cols-2 gap-4">
@@ -451,7 +451,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100 mb-6">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:banknotes" class="w-4 h-4" />
<Icon name="banknotes" class="w-4 h-4" />
Revenue by Client
</h2>
<div class="overflow-x-auto">
@@ -493,7 +493,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:chart-pie" class="w-4 h-4" />
<Icon name="chart-pie" class="w-4 h-4" />
Tag Distribution
</h2>
<div class="h-64 w-full">
@@ -515,7 +515,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:chart-bar" class="w-4 h-4" />
<Icon name="chart-bar" class="w-4 h-4" />
Time by Client
</h2>
<div class="h-64 w-full">
@@ -537,7 +537,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100 mb-6">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:users" class="w-4 h-4" />
<Icon name="users" class="w-4 h-4" />
Time by Team Member
</h2>
<div class="h-64 w-full">
@@ -560,7 +560,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100 mb-6">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:users" class="w-4 h-4" />
<Icon name="users" class="w-4 h-4" />
By Team Member
</h2>
<div class="overflow-x-auto">
@@ -601,7 +601,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100 mb-6">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:tag" class="w-4 h-4" />
<Icon name="tag" class="w-4 h-4" />
By Tag
</h2>
<div class="overflow-x-auto">
@@ -653,7 +653,7 @@ function getTimeRangeLabel(range: string) {
<div class="card card-border bg-base-100 mb-6">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-3">
<Icon name="heroicons:building-office" class="w-4 h-4" />
<Icon name="building-office" class="w-4 h-4" />
By Client
</h2>
<div class="overflow-x-auto">
@@ -698,12 +698,12 @@ function getTimeRangeLabel(range: string) {
<div class="card-body p-4">
<div class="flex justify-between items-center mb-3">
<h2 class="text-sm font-semibold flex items-center gap-2">
<Icon name="heroicons:document-text" class="w-4 h-4" />
<Icon name="document-text" class="w-4 h-4" />
Detailed Entries ({entries.length})
</h2>
{entries.length > 0 && (
<a href={`/api/reports/export${url.search}`} class="btn btn-xs btn-ghost" target="_blank">
<Icon name="heroicons:arrow-down-tray" class="w-3.5 h-3.5" />
<Icon name="arrow-down-tray" class="w-3.5 h-3.5" />
Export CSV
</a>
)}
@@ -758,11 +758,11 @@ function getTimeRangeLabel(range: string) {
</div>
) : (
<div class="flex flex-col items-center justify-center py-10 text-center">
<Icon name="heroicons:inbox" class="w-12 h-12 text-base-content/15 mb-3" />
<Icon name="inbox" class="w-12 h-12 text-base-content/15 mb-3" />
<h3 class="text-base font-semibold mb-1">No time entries found</h3>
<p class="text-base-content/50 text-sm mb-4">Try adjusting your filters or select a different time range.</p>
<a href="/dashboard/tracker" class="btn btn-primary btn-sm">
<Icon name="heroicons:play" class="w-4 h-4" />
<Icon name="play" class="w-4 h-4" />
Start Tracking Time
</a>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../components/Icon.astro';
import { db } from '../../db';
import { apiTokens, passkeys } from '../../db/schema';
import { eq, desc } from 'drizzle-orm';
@@ -37,14 +37,14 @@ const userPasskeys = await db.select()
{/* Success Messages */}
{successType === 'profile' && (
<div class="alert alert-success mb-6">
<Icon name="heroicons:check-circle" class="w-5 h-5 sm:w-6 sm:h-6 shrink-0" />
<Icon name="check-circle" class="w-5 h-5 sm:w-6 sm:h-6 shrink-0" />
<span class="text-sm sm:text-base">Profile updated successfully!</span>
</div>
)}
{successType === 'password' && (
<div class="alert alert-success mb-6">
<Icon name="heroicons:check-circle" class="w-5 h-5 sm:w-6 sm:h-6 shrink-0" />
<Icon name="check-circle" class="w-5 h-5 sm:w-6 sm:h-6 shrink-0" />
<span class="text-sm sm:text-base">Password changed successfully!</span>
</div>
)}
@@ -72,7 +72,7 @@ const userPasskeys = await db.select()
<div class="card card-border bg-base-100">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-4">
<Icon name="heroicons:information-circle" class="w-4 h-4" />
<Icon name="information-circle" class="w-4 h-4" />
Account Information
</h2>

View File

@@ -1,7 +1,7 @@
---
import DashboardLayout from '../../layouts/DashboardLayout.astro';
import Avatar from '../../components/Avatar.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../components/Icon.astro';
import { db } from '../../db';
import { members, users } from '../../db/schema';
import { eq } from 'drizzle-orm';
@@ -36,7 +36,7 @@ const isAdmin = currentUserMember?.member.role === 'owner' || currentUserMember?
{isAdmin && (
<>
<a href="/dashboard/team/settings" class="btn btn-ghost btn-sm">
<Icon name="heroicons:cog-6-tooth" class="w-4 h-4" />
<Icon name="cog-6-tooth" class="w-4 h-4" />
Settings
</a>
<a href="/dashboard/team/invite" class="btn btn-primary btn-sm">Invite Member</a>
@@ -88,7 +88,7 @@ const isAdmin = currentUserMember?.member.role === 'owner' || currentUserMember?
{teamUser.id !== user.id && member.role !== 'owner' && (
<div class="dropdown dropdown-end">
<div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square">
<Icon name="heroicons:ellipsis-vertical" class="w-4 h-4" />
<Icon name="ellipsis-vertical" class="w-4 h-4" />
</div>
<ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-200">
<li>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../components/Icon.astro';
import { db } from '../../../db';
import { members } from '../../../db/schema';
import { eq } from 'drizzle-orm';
@@ -34,7 +34,7 @@ if (!isAdmin) return Astro.redirect('/dashboard/team');
<form method="POST" action="/api/team/invite" class="card card-border bg-base-100">
<div class="card-body p-4">
<div class="alert alert-info mb-4">
<Icon name="heroicons:information-circle" class="w-4 h-4 shrink-0" />
<Icon name="information-circle" class="w-4 h-4 shrink-0" />
<span class="text-sm">The user must already have an account. They'll be added to your organization.</span>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../../components/Icon.astro';
import { db } from '../../../db';
import { organizations, tags } from '../../../db/schema';
import { eq } from 'drizzle-orm';
@@ -38,7 +38,7 @@ const successType = url.searchParams.get('success');
<DashboardLayout title="Team Settings - Chronus">
<div class="flex items-center gap-3 mb-6">
<a href="/dashboard/team" class="btn btn-ghost btn-xs">
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
<Icon name="arrow-left" class="w-4 h-4" />
</a>
<h1 class="text-2xl font-extrabold tracking-tight">Team Settings</h1>
</div>
@@ -47,13 +47,13 @@ const successType = url.searchParams.get('success');
<div class="card card-border bg-base-100 mb-6">
<div class="card-body p-4">
<h2 class="text-sm font-semibold flex items-center gap-2 mb-4">
<Icon name="heroicons:building-office-2" class="w-4 h-4" />
<Icon name="building-office-2" class="w-4 h-4" />
Team Settings
</h2>
{successType === 'org-name' && (
<div class="alert alert-success mb-4">
<Icon name="heroicons:check-circle" class="w-4 h-4" />
<Icon name="check-circle" class="w-4 h-4" />
<span class="text-sm">Team information updated successfully!</span>
</div>
)}
@@ -79,7 +79,7 @@ const successType = url.searchParams.get('success');
/>
) : (
<Icon
name="heroicons:photo"
name="photo"
class="w-6 h-6 opacity-40 text-base-content"
/>
)}
@@ -218,7 +218,7 @@ const successType = url.searchParams.get('success');
</span>
<button type="submit" class="btn btn-primary btn-sm w-full sm:w-auto">
<Icon name="heroicons:check" class="w-4 h-4" />
<Icon name="check" class="w-4 h-4" />
Save Changes
</button>
</div>
@@ -231,11 +231,11 @@ const successType = url.searchParams.get('success');
<div class="card-body p-4">
<div class="flex justify-between items-center mb-4">
<h2 class="text-sm font-semibold flex items-center gap-2">
<Icon name="heroicons:tag" class="w-4 h-4" />
<Icon name="tag" class="w-4 h-4" />
Tags & Rates
</h2>
<button onclick="document.getElementById('new_tag_modal').showModal()" class="btn btn-primary btn-xs">
<Icon name="heroicons:plus" class="w-3 h-3" />
<Icon name="plus" class="w-3 h-3" />
Add Tag
</button>
</div>
@@ -246,7 +246,7 @@ const successType = url.searchParams.get('success');
{allTags.length === 0 ? (
<div class="alert alert-info">
<Icon name="heroicons:information-circle" class="w-4 h-4" />
<Icon name="information-circle" class="w-4 h-4" />
<div>
<div class="font-semibold text-sm">No tags yet</div>
<div class="text-xs">Create tags to add context and rates to your time entries.</div>
@@ -286,11 +286,11 @@ const successType = url.searchParams.get('success');
onclick={`document.getElementById('edit_tag_modal_${tag.id}').showModal()`}
class="btn btn-ghost btn-xs btn-square"
>
<Icon name="heroicons:pencil" class="w-3 h-3" />
<Icon name="pencil" class="w-3 h-3" />
</button>
<form method="POST" action={`/api/tags/${tag.id}/delete`} onsubmit="return confirm('Are you sure you want to delete this tag?');">
<button class="btn btn-ghost btn-xs btn-square text-error">
<Icon name="heroicons:trash" class="w-3 h-3" />
<Icon name="trash" class="w-3 h-3" />
</button>
</form>
</div>

View File

@@ -1,6 +1,6 @@
---
import DashboardLayout from '../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import Icon from '../../components/Icon.astro';
import Timer from '../../components/Timer.vue';
import ManualEntry from '../../components/ManualEntry.vue';
import { db } from '../../db';
@@ -147,7 +147,7 @@ const paginationPages = getPaginationPages(page, totalPages);
<div class="tab-content bg-base-100 border-base-300 p-6">
{allClients.length === 0 ? (
<div class="alert alert-warning flex flex-col sm:flex-row items-center gap-4">
<Icon name="heroicons:exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
<Icon name="exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
<span class="flex-1 text-center sm:text-left">You need to create a client before tracking time.</span>
<a href="/dashboard/clients/new" class="btn btn-sm btn-primary whitespace-nowrap">Add Client</a>
</div>
@@ -170,7 +170,7 @@ const paginationPages = getPaginationPages(page, totalPages);
<div class="tab-content bg-base-100 border-base-300 p-6">
{allClients.length === 0 ? (
<div class="alert alert-warning flex flex-col sm:flex-row items-center gap-4">
<Icon name="heroicons:exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
<Icon name="exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
<span class="flex-1 text-center sm:text-left">You need to create a client before adding time entries.</span>
<a href="/dashboard/clients/new" class="btn btn-sm btn-primary whitespace-nowrap">Add Client</a>
</div>
@@ -247,7 +247,7 @@ const paginationPages = getPaginationPages(page, totalPages);
<input type="hidden" name="page" value="1" />
<div class="flex items-end md:col-span-2 lg:col-span-1">
<button type="submit" class="btn btn-primary btn-sm w-full">
<Icon name="heroicons:magnifying-glass" class="w-4 h-4" />
<Icon name="magnifying-glass" class="w-4 h-4" />
Search
</button>
</div>
@@ -259,12 +259,12 @@ const paginationPages = getPaginationPages(page, totalPages);
<div class="card-body p-4">
<div class="flex justify-between items-center mb-3">
<h2 class="text-sm font-semibold flex items-center gap-2">
<Icon name="heroicons:list-bullet" class="w-4 h-4" />
<Icon name="list-bullet" class="w-4 h-4" />
Time Entries ({totalCount?.count || 0} total)
</h2>
{(filterClient || filterStatus || filterType || searchTerm) && (
<a href="/dashboard/tracker" class="btn btn-xs btn-ghost">
<Icon name="heroicons:x-mark" class="w-3 h-3" />
<Icon name="x-mark" class="w-3 h-3" />
Clear Filters
</a>
)}
@@ -289,12 +289,12 @@ const paginationPages = getPaginationPages(page, totalPages);
<td>
{entry.isManual ? (
<span class="badge badge-info badge-xs gap-1" title="Manual Entry">
<Icon name="heroicons:pencil" class="w-3 h-3" />
<Icon name="pencil" class="w-3 h-3" />
Manual
</span>
) : (
<span class="badge badge-success badge-xs gap-1" title="Timed Entry">
<Icon name="heroicons:clock" class="w-3 h-3" />
<Icon name="clock" class="w-3 h-3" />
Timed
</span>
)}
@@ -328,7 +328,7 @@ const paginationPages = getPaginationPages(page, totalPages);
class="btn btn-ghost btn-xs text-error"
onclick="return confirm('Are you sure you want to delete this entry?')"
>
<Icon name="heroicons:trash" class="w-3.5 h-3.5" />
<Icon name="trash" class="w-3.5 h-3.5" />
</button>
</form>
</td>
@@ -345,7 +345,7 @@ const paginationPages = getPaginationPages(page, totalPages);
href={`?page=${Math.max(1, page - 1)}${filterClient ? `&client=${filterClient}` : ''}${filterStatus ? `&status=${filterStatus}` : ''}${filterType ? `&type=${filterType}` : ''}${sortBy ? `&sort=${sortBy}` : ''}${searchTerm ? `&search=${searchTerm}` : ''}`}
class={`btn btn-xs ${page === 1 ? 'btn-disabled' : ''}`}
>
<Icon name="heroicons:chevron-left" class="w-3 h-3" />
<Icon name="chevron-left" class="w-3 h-3" />
Prev
</a>
@@ -365,7 +365,7 @@ const paginationPages = getPaginationPages(page, totalPages);
class={`btn btn-xs ${page === totalPages ? 'btn-disabled' : ''}`}
>
Next
<Icon name="heroicons:chevron-right" class="w-3 h-3" />
<Icon name="chevron-right" class="w-3 h-3" />
</a>
</div>
)}