Phase 8
This commit is contained in:
82 files changed
+4903
-287
No files matched your search
@@ -0,0 +1,49 @@
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { auth } from "@/auth";
|
||||
import { TopNav } from "@/components/navigation/top-nav";
|
||||
import { prisma } from "@/lib/db/prisma";
|
||||
import { getBranding } from "@/lib/business/branding";
|
||||
import { CalendarConnectionPanel } from "@/features/calendar/components/calendar-connection-panel";
|
||||
|
||||
export const metadata: Metadata = { title: "Google Calendar" };
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function StaffCalendarSettingsPage() {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) redirect("/sign-in?callbackUrl=/staff/settings/calendar");
|
||||
|
||||
const staffProfile = await prisma.staffProfile.findUnique({ where: { userId: session.user.id } });
|
||||
if (!staffProfile) redirect("/home");
|
||||
|
||||
const [connection, branding] = await Promise.all([
|
||||
prisma.staffCalendarConnection.findUnique({ where: { staffId: staffProfile.id } }),
|
||||
getBranding(),
|
||||
]);
|
||||
const connected = !!connection && !connection.revokedAt;
|
||||
const configured = !!process.env.GOOGLE_CALENDAR_CLIENT_ID && !!process.env.GOOGLE_CALENDAR_CLIENT_SECRET;
|
||||
|
||||
return (
|
||||
<div className="space-y-6 px-6 pt-12 pb-12">
|
||||
<TopNav title="Google Calendar" onBack sticky className="bg-stone" />
|
||||
<p className="text-muted text-sm">
|
||||
One-way sync: {branding.name} stays the source of truth, and your confirmed appointments are pushed to
|
||||
Google Calendar. Nothing is ever imported back from Google.
|
||||
</p>
|
||||
<CalendarConnectionPanel
|
||||
configured={configured}
|
||||
initial={
|
||||
connected
|
||||
? {
|
||||
connected: true,
|
||||
accountEmail: connection.accountEmail,
|
||||
selectedCalendarName: connection.selectedCalendarName,
|
||||
lastSyncAt: connection.lastSyncAt?.toISOString(),
|
||||
lastSyncError: connection.lastSyncError,
|
||||
}
|
||||
: { connected: false }
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { Bell, CalendarClock, CalendarOff, Clock, ShieldCheck, UserCircle } from "lucide-react";
|
||||
import { Bell, CalendarClock, CalendarOff, CalendarSync, Clock, ShieldCheck, UserCircle } from "lucide-react";
|
||||
import { auth } from "@/auth";
|
||||
import { ProfileCard } from "@/components/ui/profile-card";
|
||||
import { SignOutButton } from "@/features/auth/components/sign-out-button";
|
||||
import { hasPermission } from "@/lib/auth/permissions";
|
||||
import { Permission } from "@/generated/prisma/enums";
|
||||
import { getBranding } from "@/lib/business/branding";
|
||||
import { InstallAppButton } from "@/features/pwa/components/install-app-button";
|
||||
|
||||
const SETTINGS_LINKS = [
|
||||
@@ -14,6 +15,7 @@ const SETTINGS_LINKS = [
|
||||
{ href: "/staff/settings/time-off", label: "Time Off", icon: CalendarOff },
|
||||
{ href: "/staff/settings/profile", label: "Profile", icon: UserCircle },
|
||||
{ href: "/staff/settings/notifications", label: "Notifications", icon: Bell },
|
||||
{ href: "/staff/settings/calendar", label: "Google Calendar", icon: CalendarSync },
|
||||
] as const;
|
||||
|
||||
export const metadata: Metadata = { title: "Staff Settings" };
|
||||
@@ -25,7 +27,10 @@ export default async function StaffSettingsPage() {
|
||||
// Real permission check, not the templates' unconditional mock "Switch to
|
||||
// Admin View" button (see docs/implementation-plan.md §3) — only staff
|
||||
// whose Role actually grants MANAGE_STAFF see a way into the owner area.
|
||||
const canManageStaff = await hasPermission(session.user.id, Permission.MANAGE_STAFF);
|
||||
const [canManageStaff, branding] = await Promise.all([
|
||||
hasPermission(session.user.id, Permission.MANAGE_STAFF),
|
||||
getBranding(),
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 px-6 pt-12">
|
||||
@@ -55,7 +60,7 @@ export default async function StaffSettingsPage() {
|
||||
|
||||
<div className="rounded-jumbo border border-line bg-white p-5">
|
||||
<p className="mb-3 text-sm font-bold">Get the app</p>
|
||||
<InstallAppButton />
|
||||
<InstallAppButton businessName={branding.name} />
|
||||
</div>
|
||||
|
||||
<SignOutButton />
|
||||
|
||||
Reference in new issue
Block a user