PWA Settings
Fixed webmanifest generation and pwa icon. Made nav bar text optional.
This commit is contained in:
23 files changed
+1065
-91
No files matched your search
@@ -13,6 +13,7 @@ import {
|
||||
Gift,
|
||||
Images,
|
||||
ScissorsSquare,
|
||||
Smartphone,
|
||||
Stamp,
|
||||
Star,
|
||||
TrendingUp,
|
||||
@@ -24,7 +25,7 @@ import { auth } from "@/auth";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { TopNav } from "@/components/navigation/top-nav";
|
||||
import { prisma } from "@/lib/db/prisma";
|
||||
import { hasPermission } from "@/lib/auth/permissions";
|
||||
import { hasPermission, isSuperAdmin } from "@/lib/auth/permissions";
|
||||
import { Permission } from "@/generated/prisma/enums";
|
||||
import { getActingBusinessContext } from "@/lib/business/get-acting-business-context";
|
||||
import { todayIn } from "@/lib/time/business-time";
|
||||
@@ -48,38 +49,44 @@ function StatTile({ icon: Icon, label, value, hint }: { icon: typeof Calendar; l
|
||||
);
|
||||
}
|
||||
|
||||
const MANAGE_SECTIONS = [
|
||||
{
|
||||
title: "Business",
|
||||
links: [
|
||||
{ href: "/owner/settings/business", label: "Business Details", icon: Building2 },
|
||||
{ href: "/owner/settings/hours", label: "Opening Hours", icon: CalendarClock },
|
||||
{ href: "/owner/settings/gallery", label: "Gallery", icon: Images },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Booking",
|
||||
links: [
|
||||
{ href: "/owner/services", label: "Services", icon: ScissorsSquare },
|
||||
{ href: "/owner/settings/booking-rules", label: "Booking Rules", icon: ClipboardList },
|
||||
{ href: "/owner/settings/loyalty", label: "Loyalty Programme", icon: Crown },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Customers",
|
||||
links: [{ href: "/owner/settings/reviews", label: "Reviews", icon: Star }],
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
links: [{ href: "/owner/team", label: "Team Management", icon: Users }],
|
||||
},
|
||||
{
|
||||
title: "Reports",
|
||||
links: [{ href: "/owner/reports", label: "Reports", icon: BarChart3 }],
|
||||
},
|
||||
] as const;
|
||||
/** App Icon & Home Screen is Super Administrator only (see /owner/settings/app) — everything else here is any Owner. */
|
||||
function getManageSections(isSuper: boolean) {
|
||||
return [
|
||||
{
|
||||
title: "Business",
|
||||
links: [
|
||||
{ href: "/owner/settings/business", label: "Business Details", icon: Building2 },
|
||||
{ href: "/owner/settings/hours", label: "Opening Hours", icon: CalendarClock },
|
||||
{ href: "/owner/settings/gallery", label: "Gallery", icon: Images },
|
||||
...(isSuper
|
||||
? [{ href: "/owner/settings/app", label: "App Icon & Home Screen", icon: Smartphone }]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Booking",
|
||||
links: [
|
||||
{ href: "/owner/services", label: "Services", icon: ScissorsSquare },
|
||||
{ href: "/owner/settings/booking-rules", label: "Booking Rules", icon: ClipboardList },
|
||||
{ href: "/owner/settings/loyalty", label: "Loyalty Programme", icon: Crown },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Customers",
|
||||
links: [{ href: "/owner/settings/reviews", label: "Reviews", icon: Star }],
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
links: [{ href: "/owner/team", label: "Team Management", icon: Users }],
|
||||
},
|
||||
{
|
||||
title: "Reports",
|
||||
links: [{ href: "/owner/reports", label: "Reports", icon: BarChart3 }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function ManageSection({ title, links }: (typeof MANAGE_SECTIONS)[number]) {
|
||||
function ManageSection({ title, links }: ReturnType<typeof getManageSections>[number]) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-muted mb-2 px-1 text-[10px] font-bold tracking-widest uppercase">{title}</p>
|
||||
@@ -108,14 +115,16 @@ export default async function OwnerDashboardPage() {
|
||||
if (!context) redirect("/staff");
|
||||
|
||||
const today = todayIn(context.businessTimezone);
|
||||
const [summary, unreadNotificationCount] = await Promise.all([
|
||||
const [summary, unreadNotificationCount, isSuper] = await Promise.all([
|
||||
getDashboardSummary(prisma, {
|
||||
businessId: context.businessId,
|
||||
businessTimezone: context.businessTimezone,
|
||||
date: today,
|
||||
}),
|
||||
getUnreadNotificationCount(prisma, session.user.id),
|
||||
isSuperAdmin(session.user.id),
|
||||
]);
|
||||
const manageSections = getManageSections(isSuper);
|
||||
|
||||
return (
|
||||
<div className="pb-12">
|
||||
@@ -237,7 +246,7 @@ export default async function OwnerDashboardPage() {
|
||||
</Card>
|
||||
|
||||
<div className="space-y-5">
|
||||
{MANAGE_SECTIONS.map((section) => (
|
||||
{manageSections.map((section) => (
|
||||
<ManageSection key={section.title} {...section} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { auth } from "@/auth";
|
||||
import { SettingsPageShell } from "@/components/settings/settings-page-shell";
|
||||
import { prisma } from "@/lib/db/prisma";
|
||||
import { isSuperAdmin } from "@/lib/auth/permissions";
|
||||
import { getActingBusinessContext } from "@/lib/business/get-acting-business-context";
|
||||
import { parseSplashScreens } from "@/lib/business/branding";
|
||||
import { getBusinessProfile } from "@/services/business/business.service";
|
||||
import { AppIconSettingsForm } from "@/features/owner/components/app-icon-settings-form";
|
||||
|
||||
export const metadata: Metadata = { title: "App Icon & Home Screen" };
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** Super Administrator only — not Owner, unlike every other /owner/settings/* page. See requireSuperAdminBusinessManager in features/owner/actions/business.ts for the matching action-layer check. */
|
||||
export default async function OwnerAppIconSettingsPage() {
|
||||
const session = await auth();
|
||||
if (!session?.user) redirect("/sign-in");
|
||||
|
||||
if (!(await isSuperAdmin(session.user.id))) redirect("/owner");
|
||||
|
||||
const context = await getActingBusinessContext();
|
||||
if (!context) redirect("/staff");
|
||||
|
||||
const business = await getBusinessProfile(prisma, context.businessId);
|
||||
if (!business) redirect("/owner");
|
||||
|
||||
return (
|
||||
<SettingsPageShell title="App Icon & Home Screen">
|
||||
<AppIconSettingsForm
|
||||
initial={{
|
||||
shortName: business.shortName?.trim() || business.name,
|
||||
appIconUrl: business.appIconUrl,
|
||||
themeColor: business.themeColor,
|
||||
backgroundColor: business.backgroundColor,
|
||||
icon192Url: business.icon192Url,
|
||||
icon512Url: business.icon512Url,
|
||||
maskableIcon192Url: business.maskableIcon192Url,
|
||||
maskableIcon512Url: business.maskableIcon512Url,
|
||||
splashScreens: parseSplashScreens(business.splashScreens),
|
||||
bottomNavShowLabels: business.bottomNavShowLabels,
|
||||
}}
|
||||
/>
|
||||
</SettingsPageShell>
|
||||
);
|
||||
}
|
||||
@@ -38,9 +38,6 @@ export default async function OwnerBusinessDetailsPage() {
|
||||
welcomeBackgroundDesktopUrl: business.welcomeBackgroundDesktopUrl,
|
||||
welcomeBackgroundMobileUrl: business.welcomeBackgroundMobileUrl,
|
||||
faviconUrl: business.faviconUrl,
|
||||
appIconUrl: business.appIconUrl,
|
||||
themeColor: business.themeColor,
|
||||
backgroundColor: business.backgroundColor,
|
||||
phone: business.phone ?? "",
|
||||
email: business.email ?? "",
|
||||
website: business.website ?? "",
|
||||
|
||||
Reference in new issue
Block a user