First pass

This commit is contained in:
2025-12-25 22:10:06 -07:00
parent a2af6195f9
commit 455c3dbd9a
58 changed files with 10299 additions and 3 deletions

View File

@@ -0,0 +1,68 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import { db } from '../../../db';
import { members } from '../../../db/schema';
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))
.get();
if (!userMembership) return Astro.redirect('/dashboard');
const isAdmin = userMembership.role === 'owner' || userMembership.role === 'admin';
if (!isAdmin) return Astro.redirect('/dashboard/team');
---
<DashboardLayout title="Invite Team Member - Zamaan">
<div class="max-w-2xl mx-auto">
<h1 class="text-3xl font-bold mb-6">Invite Team Member</h1>
<form method="POST" action="/api/team/invite" class="card bg-base-100 shadow-xl border border-base-200">
<div class="card-body">
<div class="alert alert-info mb-4">
<Icon name="heroicons:information-circle" class="w-6 h-6" />
<span>The user must already have an account. They'll be added to your organization.</span>
</div>
<div class="form-control">
<label class="label" for="email">
<span class="label-text">Email Address</span>
</label>
<input
type="email"
id="email"
name="email"
placeholder="user@example.com"
class="input input-bordered"
required
/>
</div>
<div class="form-control">
<label class="label" for="role">
<span class="label-text">Role</span>
</label>
<select id="role" name="role" class="select select-bordered" required>
<option value="member">Member</option>
<option value="admin">Admin</option>
</select>
<label class="label">
<span class="label-text-alt">Members can track time. Admins can manage team and clients.</span>
</label>
</div>
<div class="card-actions justify-end mt-6">
<a href="/dashboard/team" class="btn btn-ghost">Cancel</a>
<button type="submit" class="btn btn-primary">Invite Member</button>
</div>
</div>
</form>
</div>
</DashboardLayout>

View File

@@ -0,0 +1,105 @@
---
import DashboardLayout from '../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import { db } from '../../../db';
import { categories, members } from '../../../db/schema';
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))
.get();
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))
.all();
---
<DashboardLayout title="Team Settings - Zamaan">
<div class="flex items-center gap-3 mb-6">
<a href="/dashboard/team" class="btn btn-ghost btn-sm">
<Icon name="heroicons:arrow-left" class="w-5 h-5" />
</a>
<h1 class="text-3xl font-bold">Team Settings</h1>
</div>
<!-- Categories Section -->
<div class="card bg-base-100 shadow-xl border border-base-200 mb-6">
<div class="card-body">
<div class="flex justify-between items-center mb-4">
<h2 class="card-title">
<Icon name="heroicons:tag" class="w-6 h-6" />
Work Categories
</h2>
<a href="/dashboard/team/settings/categories/new" class="btn btn-primary btn-sm">
<Icon name="heroicons:plus" class="w-5 h-5" />
Add Category
</a>
</div>
<p class="text-base-content/70 mb-4">
Categories help organize time tracking by type of work. All team members use the same categories.
</p>
{allCategories.length === 0 ? (
<div class="alert alert-info">
<Icon name="heroicons:information-circle" class="w-6 h-6" />
<div>
<div class="font-bold">No categories yet</div>
<div class="text-sm">Create your first category to start organizing time entries.</div>
</div>
</div>
) : (
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{allCategories.map(category => (
<div class="card bg-base-200 border border-base-300">
<div class="card-body p-4">
<div class="flex items-center gap-3">
{category.color && (
<span class="w-4 h-4 rounded-full flex-shrink-0" style={`background-color: ${category.color}`}></span>
)}
<div class="flex-grow min-w-0">
<h3 class="font-semibold truncate">{category.name}</h3>
<p class="text-xs text-base-content/60">
Created {category.createdAt?.toLocaleDateString() ?? 'N/A'}
</p>
</div>
<a
href={`/dashboard/team/settings/categories/${category.id}/edit`}
class="btn btn-ghost btn-xs"
>
<Icon name="heroicons:pencil" class="w-4 h-4" />
</a>
</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
<!-- Future Settings Sections -->
<div class="card bg-base-100 shadow-xl border border-base-200">
<div class="card-body">
<h2 class="card-title">
<Icon name="heroicons:cog-6-tooth" class="w-6 h-6" />
Organization Settings
</h2>
<p class="text-base-content/70">
Additional organization settings coming soon...
</p>
</div>
</div>
</DashboardLayout>

View File

@@ -0,0 +1,87 @@
---
import DashboardLayout from '../../../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
import { db } from '../../../../../db';
import { categories, members } from '../../../../../db/schema';
import { eq, and } from 'drizzle-orm';
const user = Astro.locals.user;
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))
.get();
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(
eq(categories.id, id!),
eq(categories.organizationId, userMembership.organizationId)
))
.get();
if (!category) return Astro.redirect('/dashboard/team/settings');
---
<DashboardLayout title="Edit Category - Zamaan">
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-3 mb-6">
<a href="/dashboard/team/settings" class="btn btn-ghost btn-sm">
<Icon name="heroicons:arrow-left" class="w-5 h-5" />
</a>
<h1 class="text-3xl font-bold">Edit Category</h1>
</div>
<form method="POST" action={`/api/categories/${id}/update`} class="card bg-base-200 shadow-xl border border-base-300">
<div class="card-body">
<div class="form-control">
<label class="label pb-2" for="name">
<span class="label-text font-medium">Category Name</span>
</label>
<input
type="text"
id="name"
name="name"
value={category.name}
placeholder="Development"
class="input input-bordered w-full"
required
/>
</div>
<div class="form-control">
<label class="label pb-2" for="color">
<span class="label-text font-medium">Color (optional)</span>
</label>
<input
type="color"
id="color"
name="color"
value={category.color || '#3b82f6'}
class="input input-bordered w-full h-12"
/>
</div>
<div class="card-actions justify-between mt-6">
<form method="POST" action={`/api/categories/${id}/delete`}>
<button type="submit" class="btn btn-error btn-outline">Delete Category</button>
</form>
<div class="flex gap-2">
<a href="/dashboard/team/settings" class="btn btn-ghost">Cancel</a>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</div>
</div>
</form>
</div>
</DashboardLayout>

View File

@@ -0,0 +1,53 @@
---
import DashboardLayout from '../../../../../layouts/DashboardLayout.astro';
import { Icon } from 'astro-icon/components';
const user = Astro.locals.user;
if (!user) return Astro.redirect('/login');
---
<DashboardLayout title="New Category - Zamaan">
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-3 mb-6">
<a href="/dashboard/team/settings" class="btn btn-ghost btn-sm">
<Icon name="heroicons:arrow-left" class="w-5 h-5" />
</a>
<h1 class="text-3xl font-bold">Add New Category</h1>
</div>
<form method="POST" action="/api/categories/create" class="card bg-base-200 shadow-xl border border-base-300">
<div class="card-body">
<div class="form-control">
<label class="label pb-2" for="name">
<span class="label-text font-medium">Category Name</span>
</label>
<input
type="text"
id="name"
name="name"
placeholder="Development"
class="input input-bordered w-full"
required
/>
</div>
<div class="form-control">
<label class="label pb-2" for="color">
<span class="label-text font-medium">Color (optional)</span>
</label>
<input
type="color"
id="color"
name="color"
class="input input-bordered w-full h-12"
/>
</div>
<div class="card-actions justify-end mt-6">
<a href="/dashboard/team/settings" class="btn btn-ghost">Cancel</a>
<button type="submit" class="btn btn-primary">Create Category</button>
</div>
</div>
</form>
</div>
</DashboardLayout>