diff --git a/src/lib/components/DashboardSection.svelte b/src/lib/components/DashboardSection.svelte
new file mode 100644
index 0000000..f176d8f
--- /dev/null
+++ b/src/lib/components/DashboardSection.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
{title}
+ {#if description}
+
{description}
+ {/if}
+
+
+ {@render children()}
+
+
diff --git a/src/routes/(protected)/(user)/dashboard/+page.server.ts b/src/routes/(protected)/(user)/dashboard/+page.server.ts
index 8e187e0..ff23f60 100644
--- a/src/routes/(protected)/(user)/dashboard/+page.server.ts
+++ b/src/routes/(protected)/(user)/dashboard/+page.server.ts
@@ -7,23 +7,10 @@ export const load: PageServerLoad = async ({ locals }) => {
throw error(403, 'not authorized');
}
- const profile = await prisma.profile.findFirst({
+ const licenseKeys = await prisma.licenseKey.findMany({
where: { userId: locals.user.id },
- select: { firstName: true }
+ orderBy: { createdAt: 'desc' }
});
- const keyStats = await prisma.licenseKey.groupBy({
- by: ['active'],
- where: { userId: locals.user.id },
- _count: true
- });
-
- const activeKeys = keyStats.find((s) => s.active)?._count ?? 0;
- const totalKeys = keyStats.reduce((sum, s) => sum + s._count, 0);
-
- return {
- firstName: profile?.firstName ?? 'User',
- activeKeys,
- totalKeys
- };
+ return { licenseKeys };
};
diff --git a/src/routes/(protected)/(user)/dashboard/+page.svelte b/src/routes/(protected)/(user)/dashboard/+page.svelte
index 028a834..8675bc9 100644
--- a/src/routes/(protected)/(user)/dashboard/+page.svelte
+++ b/src/routes/(protected)/(user)/dashboard/+page.svelte
@@ -1,33 +1,90 @@
-
- Welcome, {data.firstName}!
-
-
Here's an overview of your account.
-
-
diff --git a/src/routes/(protected)/(user)/dashboard/page.server.spec.ts b/src/routes/(protected)/(user)/dashboard/page.server.spec.ts
index fe27011..6d861ba 100644
--- a/src/routes/(protected)/(user)/dashboard/page.server.spec.ts
+++ b/src/routes/(protected)/(user)/dashboard/page.server.spec.ts
@@ -1,24 +1,21 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { isHttpError } from '@sveltejs/kit';
-import { createLocals, mockUser } from '$lib/test-utils';
+import { createLocals, mockUser, mockLicenseKey } from '$lib/test-utils';
vi.mock('$lib/db', () => ({
prisma: {
- profile: { findFirst: vi.fn() },
- licenseKey: { groupBy: vi.fn() }
+ licenseKey: { findMany: vi.fn() }
}
}));
import { prisma } from '$lib/db';
import { load } from './+page.server';
-const profileFindFirst = prisma.profile.findFirst as ReturnType
;
-const licenseKeyGroupBy = prisma.licenseKey.groupBy as ReturnType;
+const licenseKeyFindMany = prisma.licenseKey.findMany as ReturnType;
describe('(protected)/dashboard page server load', () => {
beforeEach(() => {
- profileFindFirst.mockReset();
- licenseKeyGroupBy.mockReset();
+ licenseKeyFindMany.mockReset();
});
it('throws 403 when locals.user is undefined', async () => {
@@ -28,44 +25,35 @@ describe('(protected)/dashboard page server load', () => {
expect(isHttpError(err, 403)).toBe(true);
});
- it('returns firstName from profile', async () => {
- profileFindFirst.mockResolvedValue({ firstName: 'Jane' });
- licenseKeyGroupBy.mockResolvedValue([]);
+ it('returns license keys for the authenticated user', async () => {
+ const keys = [
+ mockLicenseKey(),
+ mockLicenseKey({ id: 'key-2', key: 'EEEE-FFFF-GGGG-HHHH', active: false })
+ ];
+ licenseKeyFindMany.mockResolvedValue(keys);
const locals = createLocals(mockUser() as any);
const result = (await load({ locals } as any)) as any;
- expect(result.firstName).toBe('Jane');
+ expect(result.licenseKeys).toEqual(keys);
});
- it('defaults firstName to "User" when profile is null', async () => {
- profileFindFirst.mockResolvedValue(null);
- licenseKeyGroupBy.mockResolvedValue([]);
+ it('returns empty array when user has no keys', async () => {
+ licenseKeyFindMany.mockResolvedValue([]);
const locals = createLocals(mockUser() as any);
const result = (await load({ locals } as any)) as any;
- expect(result.firstName).toBe('User');
+ expect(result.licenseKeys).toEqual([]);
});
- it('computes activeKeys and totalKeys from groupBy results', async () => {
- profileFindFirst.mockResolvedValue({ firstName: 'Jane' });
- licenseKeyGroupBy.mockResolvedValue([
- { active: true, _count: 3 },
- { active: false, _count: 2 }
- ]);
+ it('loads license keys filtered by user, newest first', async () => {
+ licenseKeyFindMany.mockResolvedValue([]);
const locals = createLocals(mockUser() as any);
- const result = (await load({ locals } as any)) as any;
- expect(result.activeKeys).toBe(3);
- expect(result.totalKeys).toBe(5);
- });
+ await load({ locals } as any);
- it('returns 0 for activeKeys when none are active', async () => {
- profileFindFirst.mockResolvedValue({ firstName: 'Jane' });
- licenseKeyGroupBy.mockResolvedValue([{ active: false, _count: 4 }]);
-
- const locals = createLocals(mockUser() as any);
- const result = (await load({ locals } as any)) as any;
- expect(result.activeKeys).toBe(0);
- expect(result.totalKeys).toBe(4);
+ expect(licenseKeyFindMany).toHaveBeenCalledWith({
+ where: { userId: 'user-1' },
+ orderBy: { createdAt: 'desc' }
+ });
});
});
diff --git a/src/routes/(protected)/(user)/license-keys/+page.server.ts b/src/routes/(protected)/(user)/license-keys/+page.server.ts
deleted file mode 100644
index b684dec..0000000
--- a/src/routes/(protected)/(user)/license-keys/+page.server.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { PageServerLoad } from './$types';
-import { prisma } from '$lib/db';
-import { error } from '@sveltejs/kit';
-
-export const load: PageServerLoad = async ({ locals }) => {
- if (!locals.user?.id) {
- throw error(403, 'not authorized');
- }
-
- const licenseKeys = await prisma.licenseKey.findMany({
- where: { userId: locals.user.id },
- orderBy: { key: 'asc' }
- });
-
- return { licenseKeys };
-};
diff --git a/src/routes/(protected)/(user)/license-keys/+page.svelte b/src/routes/(protected)/(user)/license-keys/+page.svelte
deleted file mode 100644
index 7767494..0000000
--- a/src/routes/(protected)/(user)/license-keys/+page.svelte
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
License Keys
-
Your active and inactive license keys.
-
-
-
- {#if data.licenseKeys.length > 0}
-
-
-
-
-
- | Key |
- Status |
-
-
-
- {#each data.licenseKeys as key (key.key)}
-
- | {key.key} |
-
-
- {key.active ? 'Active' : 'Inactive'}
-
- |
-
- {/each}
-
-
-
-
-
- {#each data.licenseKeys as key (key.key)}
- -
- {key.key}
-
- {key.active ? 'Active' : 'Inactive'}
-
-
- {/each}
-
-
- {:else}
-
-
You don't have any license keys yet.
-
- {/if}
-
diff --git a/src/routes/(protected)/(user)/license-keys/page.server.spec.ts b/src/routes/(protected)/(user)/license-keys/page.server.spec.ts
deleted file mode 100644
index 5e30c24..0000000
--- a/src/routes/(protected)/(user)/license-keys/page.server.spec.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { isHttpError } from '@sveltejs/kit';
-import { createLocals, mockUser, mockLicenseKey } from '$lib/test-utils';
-
-vi.mock('$lib/db', () => ({
- prisma: {
- licenseKey: { findMany: vi.fn() }
- }
-}));
-
-import { prisma } from '$lib/db';
-import { load } from './+page.server';
-
-const findMany = prisma.licenseKey.findMany as ReturnType;
-
-describe('(protected)/license-keys page server load', () => {
- beforeEach(() => {
- findMany.mockReset();
- });
-
- it('throws 403 when locals.user is undefined', async () => {
- const err = await Promise.resolve(load({ locals: createLocals() } as any)).catch(
- (e: unknown) => e
- );
- expect(isHttpError(err, 403)).toBe(true);
- });
-
- it('returns license keys for the authenticated user', async () => {
- const keys = [
- mockLicenseKey(),
- mockLicenseKey({ id: 'key-2', key: 'EEEE-FFFF-GGGG-HHHH', active: false })
- ];
- findMany.mockResolvedValue(keys);
-
- const locals = createLocals(mockUser() as any);
- const result = (await load({ locals } as any)) as any;
- expect(result.licenseKeys).toEqual(keys);
- });
-
- it('passes userId in the where clause', async () => {
- findMany.mockResolvedValue([]);
-
- const locals = createLocals(mockUser() as any);
- await load({ locals } as any);
-
- expect(findMany).toHaveBeenCalledWith({
- where: { userId: 'user-1' },
- orderBy: { key: 'asc' }
- });
- });
-
- it('returns empty array when user has no keys', async () => {
- findMany.mockResolvedValue([]);
-
- const locals = createLocals(mockUser() as any);
- const result = (await load({ locals } as any)) as any;
- expect(result.licenseKeys).toEqual([]);
- });
-});
diff --git a/src/routes/(protected)/(user)/account/+page.server.ts b/src/routes/(protected)/(user)/profile/+page.server.ts
similarity index 100%
rename from src/routes/(protected)/(user)/account/+page.server.ts
rename to src/routes/(protected)/(user)/profile/+page.server.ts
diff --git a/src/routes/(protected)/(user)/account/+page.svelte b/src/routes/(protected)/(user)/profile/+page.svelte
similarity index 99%
rename from src/routes/(protected)/(user)/account/+page.svelte
rename to src/routes/(protected)/(user)/profile/+page.svelte
index a4fa191..2f5095a 100644
--- a/src/routes/(protected)/(user)/account/+page.svelte
+++ b/src/routes/(protected)/(user)/profile/+page.svelte
@@ -24,8 +24,8 @@
-
Account
-
Manage your profile and addresses.
+
Profile
+
Manage your account details and addresses.
diff --git a/src/routes/(protected)/(user)/account/page.server.spec.ts b/src/routes/(protected)/(user)/profile/page.server.spec.ts
similarity index 99%
rename from src/routes/(protected)/(user)/account/page.server.spec.ts
rename to src/routes/(protected)/(user)/profile/page.server.spec.ts
index c09dc29..4d48099 100644
--- a/src/routes/(protected)/(user)/account/page.server.spec.ts
+++ b/src/routes/(protected)/(user)/profile/page.server.spec.ts
@@ -26,7 +26,7 @@ const addressCreate = prisma.address.create as ReturnType
;
const invokeAction = (name: string, request: Request, locals: App.Locals) =>
(actions as any)[name]({ request, locals } as any);
-describe('(protected)/account page server', () => {
+describe('(protected)/profile page server', () => {
beforeEach(() => {
userFindUnique.mockReset();
profileFindFirst.mockReset();
diff --git a/src/routes/(protected)/(user)/account/setup/+page.server.ts b/src/routes/(protected)/(user)/profile/setup/+page.server.ts
similarity index 100%
rename from src/routes/(protected)/(user)/account/setup/+page.server.ts
rename to src/routes/(protected)/(user)/profile/setup/+page.server.ts
diff --git a/src/routes/(protected)/(user)/account/setup/+page.svelte b/src/routes/(protected)/(user)/profile/setup/+page.svelte
similarity index 100%
rename from src/routes/(protected)/(user)/account/setup/+page.svelte
rename to src/routes/(protected)/(user)/profile/setup/+page.svelte
diff --git a/src/routes/(protected)/(user)/account/setup/page.server.spec.ts b/src/routes/(protected)/(user)/profile/setup/page.server.spec.ts
similarity index 96%
rename from src/routes/(protected)/(user)/account/setup/page.server.spec.ts
rename to src/routes/(protected)/(user)/profile/setup/page.server.spec.ts
index 5a62593..0368421 100644
--- a/src/routes/(protected)/(user)/account/setup/page.server.spec.ts
+++ b/src/routes/(protected)/(user)/profile/setup/page.server.spec.ts
@@ -27,7 +27,7 @@ import { load, actions } from './+page.server';
const invokeDefault = (request: Request) => actions.default({ request } as any);
-describe('account setup page server load', () => {
+describe('profile setup page server load', () => {
beforeEach(() => {
create.mockReset();
});
@@ -48,7 +48,7 @@ describe('account setup page server load', () => {
});
});
-describe('account setup page server action', () => {
+describe('profile setup page server action', () => {
beforeEach(() => {
create.mockReset();
});
diff --git a/src/routes/(protected)/+layout.server.ts b/src/routes/(protected)/+layout.server.ts
index 8f3c813..351f7c4 100644
--- a/src/routes/(protected)/+layout.server.ts
+++ b/src/routes/(protected)/+layout.server.ts
@@ -14,9 +14,9 @@ export const load: LayoutServerLoad = async ({ locals, url }) => {
if (!user) {
throw error(403, 'not authorized');
}
- if (user.profiles.length === 0 && !url.pathname.startsWith('/account/setup')) {
- // Skip redirect when already on /account/setup to avoid redirect loop
- throw redirect(303, '/account/setup');
+ if (user.profiles.length === 0 && !url.pathname.startsWith('/profile/setup')) {
+ // Skip redirect when already on /profile/setup to avoid redirect loop
+ throw redirect(303, '/profile/setup');
}
return { userRoleId: user.roleId };
diff --git a/src/routes/(protected)/layout.server.spec.ts b/src/routes/(protected)/layout.server.spec.ts
index d664dc0..542480c 100644
--- a/src/routes/(protected)/layout.server.spec.ts
+++ b/src/routes/(protected)/layout.server.spec.ts
@@ -39,7 +39,7 @@ describe('(protected) layout server load', () => {
expect(isHttpError(err, 403)).toBe(true);
});
- it('redirects to /account/setup when user has no profile', async () => {
+ it('redirects to /profile/setup when user has no profile', async () => {
findUnique.mockResolvedValue({ id: 'user-1', roleId: null, profiles: [] });
const locals = createLocals(mockUser() as any);
@@ -48,15 +48,15 @@ describe('(protected) layout server load', () => {
expect(isRedirect(err)).toBe(true);
if (isRedirect(err)) {
expect(err.status).toBe(303);
- expect(err.location).toBe('/account/setup');
+ expect(err.location).toBe('/profile/setup');
}
});
- it('skips redirect when already on /account/setup', async () => {
+ it('skips redirect when already on /profile/setup', async () => {
findUnique.mockResolvedValue({ id: 'user-1', roleId: null, profiles: [] });
const locals = createLocals(mockUser() as any);
- const url = new URL('http://localhost/account/setup');
+ const url = new URL('http://localhost/profile/setup');
const result = await load({ locals, url } as any);
expect(result).toEqual({ userRoleId: null });
});
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index f7c3e12..5b145a0 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -114,16 +114,10 @@
Dashboard
- Account
-
-
- License Keys
+ Profile
{#if isAdmin}
@@ -277,18 +271,11 @@
Dashboard
- Account
-
-
- License Keys
+ Profile
{#if isAdmin}
@@ -382,8 +369,8 @@
>
© {new Date().getFullYear()} Radiant Wave Technologies. All rights reserved.
{#if $session.data?.user}
-
- Manage your account
+
+ Manage your profile
{:else}
Sign in
diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte
index c9a2a12..4a65f2e 100644
--- a/src/routes/login/+page.svelte
+++ b/src/routes/login/+page.svelte
@@ -22,7 +22,7 @@
return;
}
- window.location.href = '/account';
+ window.location.href = '/dashboard';
}
@@ -104,7 +104,7 @@