Skip to content

Commit

Permalink
Merge pull request #4 from mehdibha/dev
Browse files Browse the repository at this point in the history
Feat: User management
  • Loading branch information
mehdibha authored Jan 8, 2024
2 parents fe82931 + 466ed89 commit ec85e9e
Show file tree
Hide file tree
Showing 79 changed files with 2,463 additions and 609 deletions.
3 changes: 3 additions & 0 deletions apps/web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module.exports = {
{
hostname: "github.com",
},
{
hostname: "avatars.githubusercontent.com",
},
{
hostname: "pbs.twimg.com",
},
Expand Down
Binary file modified apps/web/public/images/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions apps/web/src/app/(app)/(dashboard)/account/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function Page() {
return (
<div>
<h2 className="text-2xl font-bold ">Account</h2>
<p className="text-muted-foreground">
Manage your account settings and preferences
</p>
</div>
);
}
50 changes: 50 additions & 0 deletions apps/web/src/app/(app)/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import React from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Tabs, TabsList, TabsTrigger } from "@palettify/ui";

interface DashboardLayoutProps {
children: React.ReactNode;
}

export default function DashboardLayout(props: DashboardLayoutProps) {
const { children } = props;

const pathname = usePathname();

return (
<Tabs value={pathname} className="container pt-10">
<TabsList className="rounded-xl">
{[
{
href: "/my-themes",
label: "My themes",
disabled: false,
},
{
href: "/saved",
label: "Likes",
disabled: true,
},
{
href: "/account",
label: "Account",
disabled: true,
},
].map((link) => (
<TabsTrigger
disabled={link.disabled}
value={link.href}
asChild={!link.disabled}
className="rounded-lg"
>
{link.disabled ? link.label : <Link href={link.href}>{link.label}</Link>}
</TabsTrigger>
))}
</TabsList>
<div className="mt-4">{children}</div>
</Tabs>
);
}
58 changes: 58 additions & 0 deletions apps/web/src/app/(app)/(dashboard)/my-themes/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ThemeCard } from "@/modules/themes/components/theme-card";
import { getUserThemes } from "@/modules/themes/services";

export default async function Page() {
const trendingThemes = await getUserThemes();

return (
<div>
<h2 className="text-2xl font-bold ">My themes</h2>
<p className="text-muted-foreground">
Here you can find all the themes you created
</p>
<div className="xs:grid-cols-2 mt-4 grid grid-cols-1 gap-4 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
{trendingThemes.map((theme, index) => {
if (theme.palettes.length === 0) return null;
const palette = theme.palettes.find(
(palette) => palette.mode === theme.defaultMode
);
if (
!palette ||
!palette.background ||
!palette.foreground ||
!palette.card ||
!palette.primary ||
!palette.secondary ||
!palette.muted ||
!palette.primaryForeground ||
!palette.secondaryForeground ||
!palette.mutedForeground ||
!palette.cardForeground ||
!palette.border
)
return null;

return (
<ThemeCard
key={index}
themeId={theme.id}
palette={{
background: palette.background,
foreground: palette.foreground,
card: palette.card,
cardForeground: palette.cardForeground,
primary: palette.primary,
primaryForeground: palette.primaryForeground,
secondary: palette.secondary,
secondaryForeground: palette.secondaryForeground,
muted: palette.muted,
mutedForeground: palette.mutedForeground,
border: palette.border,
}}
/>
);
})}
</div>
</div>
);
}
46 changes: 46 additions & 0 deletions apps/web/src/app/(app)/(dashboard)/saved/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { TabsContent } from "@palettify/ui";
import { ThemeCard } from "@/modules/themes/components/theme-card";

export default function Page() {
const themes = [
{
palette: {
background: "#fff",
foreground: "#000",
primary: "#4942E4",
secondary: "#fcba03",
card: "#3d3d54",
},
},
{
palette: {
background: "#f7f7f7",
foreground: "#333",
primary: "#009688",
secondary: "#ff5722",
card: "#c0c0c0",
},
},
{
palette: {
background: "linear-gradient(to bottom right, #e4e9ea 10%, #FFDEC1 80%)",
foreground: "#fff",
primary: "#4caf50",
secondary: "#2196f3",
card: "#2c2c2c",
},
},
];

return (
<div>
<h2 className="text-2xl font-bold ">Liked themes</h2>
<p className="text-muted-foreground">Here you can find all the themes you liked</p>
<div className="xs:grid-cols-2 mt-4 grid grid-cols-1 gap-4 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
{themes.map((theme, index) => (
<ThemeCard key={index} palette={theme.palette} displayVote={false} />
))}
</div>
</div>
);
}
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions apps/web/src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import { Footer } from "@/components/footer";
import { Header } from "@/components/header";
import { getSession } from "@/modules/auth/services";

export default async function Appayout({ children }: { children: React.ReactNode }) {
const session = await getSession();

return (
<>
<Header user={session?.user} />
<div className="min-h-[calc(100vh-64px)] pb-36">{children}</div>
<Footer />
</>
);
}
93 changes: 93 additions & 0 deletions apps/web/src/app/(app)/playground/[themeId]/form-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use client";

import React from "react";
import { useTheme } from "next-themes";
import { useForm } from "react-hook-form";
import type { ThemeWithPalettes } from "@palettify/database";
import { Form } from "@palettify/ui";

const defaultLightPalette = {
background: "#ffffff",
foreground: "#000000",
card: "#ffffff",
cardForeground: "#070708",
popover: "#ffffff",
popoverForeground: "#09090b",
primary: "#18181b",
primaryForeground: "#ffffff",
secondary: "#f4f4f5",
secondaryForeground: "#18181b",
muted: "#f4f4f5",
mutedForeground: "#71717a",
accent: "#f4f4f5",
accentForeground: "#18181b",
destructive: "#dc2828",
destructiveForeground: "#fafafa",
border: "#e4e4e7",
input: "#d6c6e8",
ring: "#a1a1aa",
};

const defaultDarkPalette = {
background: "#09090b",
foreground: "#fafafa",
card: "#09090b",
cardForeground: "#fafafa",
popover: "#09090b",
popoverForeground: "#fafafa",
primary: "#fafafa",
primaryForeground: "#18181b",
secondary: "#27272a",
secondaryForeground: "#fafafa",
muted: "#27272a",
mutedForeground: "#a1a1aa",
accent: "#27272a",
accentForeground: "#fafafa",
destructive: "#801e1e",
destructiveForeground: "#fafafa",
border: "#27272a",
input: "#27272a",
ring: "#d4d4d8",
};

const defaultRadius = 0.5;

interface FormProviderProps {
children: React.ReactNode;
theme: ThemeWithPalettes;
}

export const FormProvider = (props: FormProviderProps) => {
const { theme, children } = props;

const { setTheme } = useTheme();
const [mounted, setMounted] = React.useState(false);
const lightPalette =
theme?.palettes?.find((palette) => palette.mode === "light") ?? defaultLightPalette;
const darkPalette =
theme?.palettes?.find((palette) => palette.mode === "dark") ?? defaultDarkPalette;
const radius = theme?.radius ?? defaultRadius;

const form = useForm({
values: {
library: "shadcn",
lightPalette,
darkPalette,
radius,
defaultMode: "light",
name: theme?.name ?? "",
},
});

React.useEffect(() => {
setTheme(theme.defaultMode);
setMounted(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (!mounted) {
return null;
}

return <Form {...form}>{children}</Form>;
};
35 changes: 35 additions & 0 deletions apps/web/src/app/(app)/playground/[themeId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { ReactNode } from "react";
import { ThemeForm } from "@/modules/theme-previewer/components/theme-form";
import { getThemeById } from "@/modules/themes/services";
import { FormProvider } from "./form-provider";
import { Preview } from "./preview";

interface PlaygroundPageProps {
params: { themeId: string };
children: ReactNode;
}

export default async function PlaygroundPage(props: PlaygroundPageProps) {
const { params } = props;
const { themeId } = params;
const theme = await getThemeById(themeId, { palettes: true });

if (!theme) {
throw new Error("Theme not found");
}

return (
<FormProvider theme={theme}>
<div className="min-h-screen">
<div className="lg:w-[460px]">
<div className="container px-8">
<div className="bg-background animate-in fade-in slide-in-from-top-1 rounded-xl p-4 shadow-xl duration-150">
<ThemeForm theme={theme} />
</div>
</div>
</div>
<Preview className="animate-in fade-in slide-in-from-top-1 duration-150" />
</div>
</FormProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import { cn } from "@palettify/utils";
import { useScrollLock } from "@/hooks/use-scroll-lock";
import { Preview as LibraryPreview } from "@/modules/theme-previewer/components/preview";

export const Preview = () => {
interface PreviewProps {
className?: string;
}

export const Preview = (props: PreviewProps) => {
const { className } = props;

const [mobileView, setMobileView] = React.useState(false);
const [fullScreen, setFullScreen] = React.useState(false);
const form = useFormContext();
Expand All @@ -30,7 +36,7 @@ export const Preview = () => {
};

return (
<PreviewWrapper fullScreen={fullScreen}>
<PreviewWrapper className={className} fullScreen={fullScreen}>
<div className="bg-card h-full overflow-hidden rounded-md pb-10 shadow-xl">
<div className="relative flex w-full items-center justify-between px-2 py-1">
<span />
Expand Down Expand Up @@ -61,17 +67,19 @@ export const Preview = () => {
interface PreviewWrapperProps {
children: React.ReactNode;
fullScreen?: boolean;
className?: string;
}

const PreviewWrapper = (props: PreviewWrapperProps) => {
const { fullScreen, children } = props;
const { fullScreen, className, children } = props;
return (
<div
className={cn(
"animate-in fade-in slide-in-from-top-1 fixed right-0 top-0 hidden h-[calc(100vh)] w-[calc(100%-460px+16px)] overflow-hidden p-4 pt-[64px] lg:block",
{
"z-[100000] block h-screen w-full p-0 xl:w-full xl:pt-0": fullScreen,
}
},
className
)}
>
{children}
Expand Down
Loading

0 comments on commit ec85e9e

Please sign in to comment.