refactor: improve layout structure and consistency across components

- Refactored layout structure in multiple components to enhance consistency and maintainability by introducing outer and inner wrapper classes.
- Updated the `MonthNavHeader` component for shared month navigation functionality, improving code reuse.
- Adjusted padding and margin properties in various components to ensure a cohesive design and better responsiveness.
- Removed unnecessary padding from certain elements to streamline the layout and improve visual clarity.
This commit is contained in:
2026-03-06 17:19:31 +03:00
parent 26a9443e1b
commit 43cd3bbd7d
10 changed files with 186 additions and 150 deletions

View File

@@ -10,14 +10,15 @@ import { useEffect } from "react";
import { useAdminPage, AdminDutyList, ReassignSheet } from "@/components/admin";
import { useTranslation } from "@/i18n/use-translation";
import { useAppStore } from "@/store/app-store";
import { MonthNavHeader } from "@/components/calendar/MonthNavHeader";
import { AccessDeniedScreen } from "@/components/states/AccessDeniedScreen";
import { LoadingState } from "@/components/states/LoadingState";
import { ErrorState } from "@/components/states/ErrorState";
import { Button } from "@/components/ui/button";
import { ChevronLeft as ChevronLeftIcon, ChevronRight as ChevronRightIcon } from "lucide-react";
const PAGE_WRAPPER_CLASS =
"content-safe mx-auto flex min-h-[var(--tg-viewport-stable-height,100vh)] w-full max-w-[var(--max-width-app)] flex-col bg-background px-3 pb-6";
const OUTER_CLASS =
"content-safe min-h-[var(--tg-viewport-stable-height,100vh)] bg-background";
const INNER_CLASS =
"mx-auto flex w-full max-w-[var(--max-width-app)] flex-col";
export default function AdminPage() {
const { t, monthName } = useTranslation();
@@ -31,31 +32,37 @@ export default function AdminPage() {
if (!admin.isAllowed) {
return (
<div className={PAGE_WRAPPER_CLASS}>
<AccessDeniedScreen primaryAction="reload" />
<div className={OUTER_CLASS}>
<div className={INNER_CLASS}>
<AccessDeniedScreen primaryAction="reload" />
</div>
</div>
);
}
if (admin.adminCheckComplete === null) {
return (
<div className={PAGE_WRAPPER_CLASS}>
<div className={OUTER_CLASS}>
<div className={INNER_CLASS}>
<div className="py-4 flex flex-col items-center gap-2">
<LoadingState />
<p className="text-sm text-muted-foreground">{t("admin.loading_users")}</p>
</div>
</div>
</div>
);
}
if (admin.adminAccessDenied) {
return (
<div className={PAGE_WRAPPER_CLASS}>
<div className={OUTER_CLASS}>
<div className={INNER_CLASS}>
<div className="flex flex-col gap-4 py-6">
<p className="text-muted-foreground">
{admin.adminAccessDeniedDetail ?? t("admin.access_denied")}
</p>
</div>
</div>
</div>
);
}
@@ -64,46 +71,17 @@ export default function AdminPage() {
const year = admin.adminMonth.getFullYear();
return (
<div className={PAGE_WRAPPER_CLASS}>
<div className={OUTER_CLASS}>
<div className={INNER_CLASS}>
<header className="sticky top-[var(--app-safe-top)] z-10 flex flex-col items-center border-b border-border bg-background py-3">
<div className="flex w-full items-center justify-between px-1">
<Button
type="button"
variant="secondary"
size="icon"
className="size-10 rounded-[10px] bg-surface text-accent hover:bg-[var(--surface-hover)] focus-visible:outline-accent active:scale-95 disabled:opacity-50"
aria-label={t("nav.prev_month")}
disabled={admin.loading}
onClick={admin.onPrevMonth}
>
<ChevronLeftIcon className="size-5" aria-hidden />
</Button>
<h1
className="m-0 flex flex-col items-center justify-center gap-0 leading-none"
aria-label={`${t("admin.title")}, ${monthName(month)} ${year}`}
>
<span className="text-xs font-normal leading-none text-muted">
{year}
</span>
<span className="text-[1.1rem] font-semibold leading-tight sm:text-[1.25rem]">
{monthName(month)}
</span>
</h1>
<Button
type="button"
variant="secondary"
size="icon"
className="size-10 rounded-[10px] bg-surface text-accent hover:bg-[var(--surface-hover)] focus-visible:outline-accent active:scale-95 disabled:opacity-50"
aria-label={t("nav.next_month")}
disabled={admin.loading}
onClick={admin.onNextMonth}
>
<ChevronRightIcon className="size-5" aria-hidden />
</Button>
</div>
<p className="text-sm text-muted-foreground m-0 mt-1">
{admin.loading ? "…" : t("admin.duties_count", { count: String(admin.dutyOnly.length) })}
</p>
<MonthNavHeader
month={admin.adminMonth}
disabled={admin.loading}
onPrevMonth={admin.onPrevMonth}
onNextMonth={admin.onNextMonth}
titleAriaLabel={`${t("admin.title")}, ${monthName(month)} ${year}`}
className="w-full px-1"
/>
</header>
{admin.successMessage && (
@@ -157,6 +135,7 @@ export default function AdminPage() {
onCloseAnimationEnd={admin.closeReassign}
t={t}
/>
</div>
</div>
);
}

View File

@@ -56,15 +56,19 @@ export default function Home() {
}, [setCurrentView, setSelectedDay]);
const content = accessDenied ? (
<div className="content-safe mx-auto flex min-h-[var(--tg-viewport-stable-height,100vh)] w-full max-w-[var(--max-width-app)] flex-col bg-background px-3 pb-6">
<AccessDeniedScreen primaryAction="reload" />
<div className="content-safe min-h-[var(--tg-viewport-stable-height,100vh)] bg-background">
<div className="mx-auto flex w-full max-w-[var(--max-width-app)] flex-col">
<AccessDeniedScreen primaryAction="reload" />
</div>
</div>
) : currentView === "currentDuty" ? (
<div className="content-safe mx-auto flex min-h-[var(--tg-viewport-stable-height,100vh)] w-full max-w-[var(--max-width-app)] flex-col bg-background px-3 pb-6">
<CurrentDutyView
onBack={handleBackFromCurrentDuty}
openedFromPin={startParam === "duty"}
/>
<div className="content-safe min-h-[var(--tg-viewport-stable-height,100vh)] bg-background">
<div className="mx-auto flex w-full max-w-[var(--max-width-app)] flex-col">
<CurrentDutyView
onBack={handleBackFromCurrentDuty}
openedFromPin={startParam === "duty"}
/>
</div>
</div>
) : (
<CalendarPage isAllowed={isAllowed} initDataRaw={initDataRaw} />