diff --git a/programmerbar-web/src/lib/server/services/referral.service.ts b/programmerbar-web/src/lib/server/services/referral.service.ts index b5535e7..8f0173e 100644 --- a/programmerbar-web/src/lib/server/services/referral.service.ts +++ b/programmerbar-web/src/lib/server/services/referral.service.ts @@ -1,5 +1,5 @@ import type { Database } from '$lib/server/db/drizzle'; -import { eq, and } from 'drizzle-orm'; +import { eq, and, count } from 'drizzle-orm'; import { referrals, users, type ReferralInsert } from '$lib/server/db/schemas'; import { nanoid } from 'nanoid'; import type { ShiftService } from './shift.service'; @@ -179,4 +179,21 @@ export class ReferralService { return stats; } + + async getAllReferrals(options?: { limit?: number; offset?: number }) { + return await this.#db.query.referrals.findMany({ + orderBy: (row, { desc }) => [desc(row.createdAt)], + limit: options?.limit, + offset: options?.offset, + with: { + referrer: true, + referred: true + } + }); + } + + async countAllReferrals() { + const result = await this.#db.select({ count: count() }).from(referrals); + return result[0]?.count ?? 0; + } } diff --git a/programmerbar-web/src/routes/(app)/api/rejection/+server.ts b/programmerbar-web/src/routes/(app)/api/rejection/+server.ts new file mode 100644 index 0000000..1c3f818 --- /dev/null +++ b/programmerbar-web/src/routes/(app)/api/rejection/+server.ts @@ -0,0 +1,24 @@ +import { json } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; + +export const GET: RequestHandler = async () => { + try { + const response = await fetch('https://naas.isalman.dev/no'); + + if (!response.ok) { + return json({ error: 'Failed to fetch rejection reason' }, { status: response.status }); + } + + const data = await response.json(); + + // Add CORS headers to allow frontend to access this endpoint + return json(data, { + headers: { + 'Cache-Control': 'public, max-age=60' + } + }); + } catch (error) { + console.error('Error fetching from naas.isalman.dev:', error); + return json({ error: 'Failed to fetch rejection reason' }, { status: 500 }); + } +}; diff --git a/programmerbar-web/src/routes/(portal)/portal/admin/bruker/[id]/+page.server.ts b/programmerbar-web/src/routes/(portal)/portal/admin/bruker/[id]/+page.server.ts index d5de824..8b395b5 100644 --- a/programmerbar-web/src/routes/(portal)/portal/admin/bruker/[id]/+page.server.ts +++ b/programmerbar-web/src/routes/(portal)/portal/admin/bruker/[id]/+page.server.ts @@ -14,17 +14,16 @@ export const load: PageServerLoad = async ({ params, locals }) => { } const user = await locals.userService.findById(userId); - if (!user) { - throw error(404, 'User not found'); + if (!user) { + throw error(404, 'User not found'); } - const [userShifts, unclaimedBeers, referrals, shifts] = await Promise.all([ - await locals.shiftService.findCompletedShiftsByUserId(userId), - await locals.beerService.getTotalAvailableBeers(userId), - await locals.referralService.getReferralStats(userId), - await locals.shiftService.findUpcomingShiftsByUserId(userId), - ]); - + const [userShifts, unclaimedBeers, referrals, shifts] = await Promise.all([ + locals.shiftService.findCompletedShiftsByUserId(userId), + locals.beerService.getTotalAvailableBeers(userId), + locals.referralService.getReferralStats(userId), + locals.shiftService.findUpcomingShiftsByUserId(userId) + ]); return { user, diff --git a/programmerbar-web/src/routes/(portal)/portal/admin/refs/+page.server.ts b/programmerbar-web/src/routes/(portal)/portal/admin/refs/+page.server.ts new file mode 100644 index 0000000..7042d16 --- /dev/null +++ b/programmerbar-web/src/routes/(portal)/portal/admin/refs/+page.server.ts @@ -0,0 +1,32 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +const PAGE_SIZE = 20; + +export const load: PageServerLoad = async ({ locals, url }) => { + if (locals.user?.role !== 'board') { + throw redirect(307, '/portal'); + } + + const page = Number(url.searchParams.get('page') ?? '1'); + const requestedPage = Number.isNaN(page) || page < 1 ? 1 : page; + + const totalCount = await locals.referralService.countAllReferrals(); + const pageCount = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); + const currentPage = Math.min(requestedPage, pageCount); + + const referrals = await locals.referralService.getAllReferrals({ + limit: PAGE_SIZE, + offset: (currentPage - 1) * PAGE_SIZE + }); + + return { + referrals, + pagination: { + currentPage, + pageCount, + pageSize: PAGE_SIZE, + totalCount + } + }; +}; diff --git a/programmerbar-web/src/routes/(portal)/portal/admin/refs/+page.svelte b/programmerbar-web/src/routes/(portal)/portal/admin/refs/+page.svelte new file mode 100644 index 0000000..58d5003 --- /dev/null +++ b/programmerbar-web/src/routes/(portal)/portal/admin/refs/+page.svelte @@ -0,0 +1,236 @@ + + + + Admin - Referrals + + +
+ +
+ +
+ Referrals +

Oversikt over alle referrals i systemet

+
+
+ +
+
+

Alle referrals

+

+ Viser {data.referrals.length} av {data.pagination.totalCount} totalt. +

+
+ {#if data.referrals.length === 0} +
+ Det er ingen registrerte referrals i databasen ennå. +
+ {:else} +
+ + + + + + + + + + + + {#each data.referrals as referral (referral.id)} + {@const statusBadge = getStatusBadge(referral.status)} + + + + + + + + {/each} + +
OpprettetReferrerRefereeStatusFullført
+ {formatTimestamp(referral.createdAt)} + + {referral.referrer?.name ?? 'Ukjent bruker'} + + {referral.referred?.name ?? 'Ukjent bruker'} + + + {statusBadge.label} + + + {formatTimestamp(referral.completedAt)} +
+
+
+ +
+ {/if} +
+
diff --git a/programmerbar-web/src/routes/+error.svelte b/programmerbar-web/src/routes/+error.svelte index 5aec439..8d389d6 100644 --- a/programmerbar-web/src/routes/+error.svelte +++ b/programmerbar-web/src/routes/+error.svelte @@ -10,6 +10,7 @@ TriangleAlert, Wifi } from '@lucide/svelte'; + import { onMount } from 'svelte'; type Icon = typeof CircleAlert; @@ -51,6 +52,33 @@ message: 'Noe uventet skjedde. Vennligst prøv igjen.', icon: CircleAlert }; + + let rejectionReason = $state(null); + let isLoadingReason = $state(true); + let fetchError = $state(false); + + onMount(async () => { + try { + const response = await fetch('/api/rejection'); + if (response.ok) { + const data = (await response.json()) as { reason?: string }; + if (data && data.reason) { + rejectionReason = data.reason; + } else { + console.warn('No rejection reason in response:', data); + fetchError = true; + } + } else { + console.error('Failed to fetch rejection reason, status:', response.status); + fetchError = true; + } + } catch (err) { + console.error('Failed to fetch rejection reason:', err); + fetchError = true; + } finally { + isLoadingReason = false; + } + });
@@ -59,12 +87,32 @@ class="bg-opacity-95 w-full max-w-2xl rounded-xl border-2 border-gray-300 bg-white p-8 text-center" >
- +

{page.status}

{error.title}

{error.message}

+ {#if rejectionReason} +
+

+ Avslag fra systemet +

+

"{rejectionReason}"

+
+ {:else if isLoadingReason} +
+

Henter avslagsgrunn...

+
+ {:else if fetchError} +
+

+ No as a Service +

+

Kunne ikke hente avslagsgrunn (sjekk konsollen)

+
+ {/if} + {#if page.status === 404}

Den forespurte URL-en var: