Skip to content

Commit

Permalink
feat: admin home page
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaugomez committed Sep 28, 2024
1 parent 23aa835 commit b9ac45e
Show file tree
Hide file tree
Showing 23 changed files with 177 additions and 15 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Navbar } from "@/src/common/ui/navbar/components/navbar";
* Layout for pages that have a fixed size that is equal to the width and height
* of the browser screen. The main body does not have scroll in these pages.
*/
export default function AdminLayout({
export default function FullscreenLayout({
children,
}: Readonly<{
children: React.ReactNode;
Expand Down
23 changes: 23 additions & 0 deletions app/(navbar-layout)/(login-guard)/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { fetchSession } from "@/src/auth/ui/fetch/fetch-session";
import { redirect } from "next/navigation";
import type { PropsWithChildren } from "react";

/**
* Checks that the user is an admin. Otherwise, it redirects to the home page.
*/
async function adminGuard() {
const result = await fetchSession();
if (!result.user?.isAdmin) {
redirect("/home");
}
}

/**
* Applies the `loginGuard` guard to the pages inside this layout
*/
export default async function AdminGuardLayout({
children,
}: PropsWithChildren) {
await adminGuard();
return <>{children}</>;
}
3 changes: 3 additions & 0 deletions app/(navbar-layout)/(login-guard)/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { AdminPage } from "@/src/admin/ui/pages/admin-page";

export default AdminPage;
5 changes: 2 additions & 3 deletions app/(navbar-layout)/(login-guard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { fetchSession } from "@/src/auth/ui/fetch/fetch-session";
import { redirect } from "next/navigation";
import type { PropsWithChildren } from "react";

/**
* Checks that the user is logged in and has verified the email. Otherwise, it
Expand All @@ -20,9 +21,7 @@ async function loginGuard() {
*/
export default async function LoginGuardLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
}: PropsWithChildren) {
await loginGuard();
return <>{children}</>;
}
2 changes: 1 addition & 1 deletion app/(navbar-layout)/error.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import type { ErrorPageProps } from "@/src/common/ui/models/props-with-error";
import ErrorPage from "../(admin-layout)/error";
import ErrorPage from "../(fullscreen-layout)/error";

/**
* Displays a page with an error message. Is shown when an error occurs in the pages
Expand Down
2 changes: 1 addition & 1 deletion app/auth/error.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import type { ErrorPageProps } from "@/src/common/ui/models/props-with-error";
import ErrorPage from "../(admin-layout)/error";
import ErrorPage from "../(fullscreen-layout)/error";

/**
* Displays a page with an error message. It is shown when an error occurs in
Expand Down
2 changes: 1 addition & 1 deletion app/global-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { ErrorPageProps } from "@/src/common/ui/models/props-with-error";
import { inter } from "@/src/common/ui/styles/fonts";
import { cn } from "@/src/common/ui/utils/shadcn";
import ErrorPage from "./(admin-layout)/error";
import ErrorPage from "./(fullscreen-layout)/error";

/**
* Error page that is shown when an error occurs in the `RootLayout` component.
Expand Down
6 changes: 3 additions & 3 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { textStyles } from "@/src/common/ui/styles/text-styles";
import { cn } from "@/src/common/ui/utils/shadcn";
import { SearchX } from "lucide-react";
import Link from "next/link";
import AdminLayout from "./(admin-layout)/layout";
import FullscreenLayout from "./(fullscreen-layout)/layout";

/**
* This page is shown when a user tries to access a route that does not exist.
*/
export default function NotFoundPage() {
return (
<AdminLayout>
<FullscreenLayout>
<main className="absolute inset-0 flex flex-col items-center justify-center px-4">
<div className="h-2"></div>
<SearchX className="size-8 flex-none text-slate-500" />
Expand All @@ -24,6 +24,6 @@ export default function NotFoundPage() {
</Button>
<div className="h-2"></div>
</main>
</AdminLayout>
</FullscreenLayout>
);
}
20 changes: 20 additions & 0 deletions src/admin/ui/components/admin-greeting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { textStyles } from "@/src/common/ui/styles/text-styles";
import { SlidersHorizontalIcon } from "lucide-react";

export function AdminGreeting() {
return (
<div className="px-4">
<div className="mx-auto max-w-screen-lg">
<h1 className={textStyles.h2}>
<SlidersHorizontalIcon className="mr-3 inline size-8 -translate-y-1" />
Panel de administración
</h1>
<div className="h-2" />
<p className={textStyles.muted}>
Bienvenido al panel de administración. Desde aquí puedes gestionar los
cursos, usuarios y configuración de la plataforma.
</p>
</div>
</div>
);
}
30 changes: 30 additions & 0 deletions src/admin/ui/components/admin-resource-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { textStyles } from "@/src/common/ui/styles/text-styles";
import Link from "next/link";
import type { AdminResourceModel } from "../../domain/models/admin-resource-model";
import { translateAdminKey } from "../i18n/admin-translations";
import { AdminResourceIcon } from "./admin-resource-icon";

interface AdminResourceCardProps {
resource: AdminResourceModel;
}
export function AdminResourceCard({ resource }: AdminResourceCardProps) {
return (
<Link
href={`/admin/resources/${resource.type}`}
className="block rounded-lg border border-gray-200 p-4 shadow-sm hover:bg-gray-50"
>
<AdminResourceIcon
adminResourceType={resource.type}
className="text-gray-500"
/>
<div className="h-2" />
<h4 className={textStyles.h4}>
{translateAdminKey(resource.type, "label")}
</h4>
<div className="h-1" />
<p className={textStyles.muted}>
{translateAdminKey(resource.type, "description")}
</p>
</Link>
);
}
51 changes: 51 additions & 0 deletions src/admin/ui/components/admin-resource-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { LucideProps } from "lucide-react";
import {
BanIcon,
BookIcon,
BookUserIcon,
CalendarClockIcon,
IdCardIcon,
KeyRoundIcon,
LogsIcon,
MailCheckIcon,
MailQuestionIcon,
NotebookPenIcon,
TagIcon,
TicketIcon,
UploadIcon,
UserIcon,
} from "lucide-react";
import type { ForwardRefExoticComponent } from "react";
import { AdminResourceTypeModel } from "../../domain/models/admin-resource-model";

const map: Record<
AdminResourceTypeModel,
ForwardRefExoticComponent<Omit<LucideProps, "ref">>
> = {
[AdminResourceTypeModel.tags]: TagIcon,
[AdminResourceTypeModel.reviewLogs]: LogsIcon,
[AdminResourceTypeModel.rateLimits]: BanIcon,
[AdminResourceTypeModel.courseEnrollments]: TicketIcon,
[AdminResourceTypeModel.coursePermissions]: BookUserIcon,
[AdminResourceTypeModel.courses]: BookIcon,
[AdminResourceTypeModel.emailVerificationCodes]: MailCheckIcon,
[AdminResourceTypeModel.fileUploads]: UploadIcon,
[AdminResourceTypeModel.forgotPasswordTokens]: MailQuestionIcon,
[AdminResourceTypeModel.notes]: NotebookPenIcon,
[AdminResourceTypeModel.practiceCards]: CalendarClockIcon,
[AdminResourceTypeModel.profiles]: UserIcon,
[AdminResourceTypeModel.sessions]: KeyRoundIcon,
[AdminResourceTypeModel.users]: IdCardIcon,
};

interface AdminResourceIconProps extends Omit<LucideProps, "ref"> {
adminResourceType: AdminResourceTypeModel;
}

export const AdminResourceIcon = ({
adminResourceType,
...props
}: AdminResourceIconProps) => {
const Icon = map[adminResourceType];
return <Icon {...props} />;
};
19 changes: 19 additions & 0 deletions src/admin/ui/components/admin-resources-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { textStyles } from "@/src/common/ui/styles/text-styles";
import { adminResourcesConfig } from "../../domain/config/admin-resources-config";
import { AdminResourceCard } from "./admin-resource-card";

export function AdminResourcesSection() {
return (
<div className="px-4">
<div className="mx-auto max-w-screen-lg">
<h2 className={textStyles.h3}>Gestión de recursos</h2>
<div className="h-3" />
<div className="grid gap-4 sm:grid-cols-2 md:grid-cols-3">
{adminResourcesConfig.map((resource) => (
<AdminResourceCard key={resource.type} resource={resource} />
))}
</div>
</div>
</div>
);
}
6 changes: 6 additions & 0 deletions src/admin/ui/i18n/admin-translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const adminTranslations: Record<string, string> = {};

export function translateAdminKey(...keys: string[]): string {
const key = keys.join(".");
return adminTranslations[key] || key;
}
15 changes: 15 additions & 0 deletions src/admin/ui/pages/admin-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { AdminGreeting } from "../components/admin-greeting";
import { AdminResourcesSection } from "../components/admin-resources-section";

export function AdminPage() {
return (
<main>
<div className="h-24" />
<AdminGreeting />

<div className="h-10" />

<AdminResourcesSection />
</main>
);
}
4 changes: 0 additions & 4 deletions src/admin/view/i18n/admin-translations.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/practice/ui/components/steps/practice-step-finish.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import PracticeLoadingPage from "@/app/(admin-layout)/courses/detail/[id]/practice/loading";
import PracticeLoadingPage from "@/app/(fullscreen-layout)/courses/detail/[id]/practice/loading";
import { Button } from "@/src/common/ui/components/shadcn/ui/button";
import { textStyles } from "@/src/common/ui/styles/text-styles";
import { cn } from "@/src/common/ui/utils/shadcn";
Expand Down

0 comments on commit b9ac45e

Please sign in to comment.