Remove toast components and added register form
The toast related components (toast.tsx, toaster.tsx, use-toast.ts) were removed from the code base. On the other hand, the register-form.tsx file was updated with a form for users to create an account. A new file toast-box.tsx was also added to display a custom message box.
This commit is contained in:
parent
43f9d8204c
commit
db6d838796
72
.idea/shelf/Uncommitted_changes_before_Update_at_5_16_24,_8_59_AM_[Changes]/shelved.patch
generated
Normal file
72
.idea/shelf/Uncommitted_changes_before_Update_at_5_16_24,_8_59_AM_[Changes]/shelved.patch
generated
Normal file
@ -0,0 +1,72 @@
|
||||
Index: .idea/workspace.xml
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
||||
<+><?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"AutoImportSettings\">\n <option name=\"autoReloadType\" value=\"SELECTIVE\" />\n </component>\n <component name=\"ChangeListManager\">\n <list default=\"true\" id=\"42d2b9e8-8d19-46be-8636-69ddba4519e4\" name=\"Changes\" comment=\"\">\n <change beforePath=\"$PROJECT_DIR$/package.json\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/package.json\" afterDir=\"false\" />\n <change beforePath=\"$PROJECT_DIR$/pnpm-lock.yaml\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/pnpm-lock.yaml\" afterDir=\"false\" />\n <change beforePath=\"$PROJECT_DIR$/src/app/globals.css\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/src/app/globals.css\" afterDir=\"false\" />\n <change beforePath=\"$PROJECT_DIR$/src/app/layout.tsx\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/src/app/layout.tsx\" afterDir=\"false\" />\n <change beforePath=\"$PROJECT_DIR$/src/app/page.tsx\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/src/app/page.tsx\" afterDir=\"false\" />\n <change beforePath=\"$PROJECT_DIR$/tailwind.config.ts\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/tailwind.config.ts\" afterDir=\"false\" />\n </list>\n <option name=\"SHOW_DIALOG\" value=\"false\" />\n <option name=\"HIGHLIGHT_CONFLICTS\" value=\"true\" />\n <option name=\"HIGHLIGHT_NON_ACTIVE_CHANGELIST\" value=\"false\" />\n <option name=\"LAST_RESOLUTION\" value=\"IGNORE\" />\n </component>\n <component name=\"FileTemplateManagerImpl\">\n <option name=\"RECENT_TEMPLATES\">\n <list>\n <option value=\"TypeScript File\" />\n <option value=\"TypeScript JSX File\" />\n </list>\n </option>\n </component>\n <component name=\"Git.Settings\">\n <option name=\"RECENT_GIT_ROOT_PATH\" value=\"$PROJECT_DIR$\" />\n </component>\n <component name=\"ProjectColorInfo\">{\n "associatedIndex": 2\n}</component>\n <component name=\"ProjectId\" id=\"2gVILtfjAaVovgsNiYlLfUmWgWX\" />\n <component name=\"ProjectViewState\">\n <option name=\"hideEmptyMiddlePackages\" value=\"true\" />\n <option name=\"showLibraryContents\" value=\"true\" />\n </component>\n <component name=\"PropertiesComponent\">{\n "keyToString": {\n "ASKED_ADD_EXTERNAL_FILES": "true",\n "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",\n "RunOnceActivity.ShowReadmeOnStart": "true",\n "last_opened_file_path": "/home/avnyr/workspace/Simplon/brief-06-front",\n "node.js.detected.package.eslint": "true",\n "node.js.detected.package.tslint": "true",\n "node.js.selected.package.eslint": "(autodetect)",\n "node.js.selected.package.tslint": "(autodetect)",\n "nodejs_package_manager_path": "npm",\n "npm.dev.executor": "Run",\n "settings.editor.selected.configurable": "preferences.pluginManager",\n "ts.external.directory.path": "/home/avnyr/workspace/Simplon/brief-06-front/node_modules/typescript/lib",\n "vue.rearranger.settings.migration": "true"\n }\n}</component>\n <component name=\"RunManager\">\n <configuration name=\"dev\" type=\"js.build_tools.npm\" nameIsGenerated=\"true\">\n <package-json value=\"$PROJECT_DIR$/package.json\" />\n <command value=\"run\" />\n <scripts>\n <script value=\"dev\" />\n </scripts>\n <node-interpreter value=\"project\" />\n <package-manager value=\"/usr/bin/yarn\" />\n <envs />\n <method v=\"2\" />\n </configuration>\n </component>\n <component name=\"SharedIndexes\">\n <attachedChunks>\n <set>\n <option value=\"bundled-js-predefined-1d06a55b98c1-91d5c284f522-JavaScript-WS-241.15989.105\" />\n </set>\n </attachedChunks>\n </component>\n <component name=\"SpellCheckerSettings\" RuntimeDictionaries=\"0\" Folders=\"0\" CustomDictionaries=\"0\" DefaultDictionary=\"application-level\" UseSingleDictionary=\"true\" transferred=\"true\" />\n <component name=\"TaskManager\">\n <task active=\"true\" id=\"Default\" summary=\"Default task\">\n <changelist id=\"42d2b9e8-8d19-46be-8636-69ddba4519e4\" name=\"Changes\" comment=\"\" />\n <created>1715776265663</created>\n <option name=\"number\" value=\"Default\" />\n <option name=\"presentableId\" value=\"Default\" />\n <updated>1715776265663</updated>\n <workItem from=\"1715776267209\" duration=\"3260000\" />\n <workItem from=\"1715781823049\" duration=\"3317000\" />\n </task>\n <servers />\n </component>\n <component name=\"TypeScriptGeneratedFilesManager\">\n <option name=\"version\" value=\"3\" />\n </component>\n</project>
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
|
||||
--- a/.idea/workspace.xml (revision 4559bc14311daecfe13fb72735751f1ad213dca3)
|
||||
+++ b/.idea/workspace.xml (date 1715842583417)
|
||||
@@ -4,14 +4,7 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
- <list default="true" id="42d2b9e8-8d19-46be-8636-69ddba4519e4" name="Changes" comment="">
|
||||
- <change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||
- <change beforePath="$PROJECT_DIR$/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-lock.yaml" afterDir="false" />
|
||||
- <change beforePath="$PROJECT_DIR$/src/app/globals.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/globals.css" afterDir="false" />
|
||||
- <change beforePath="$PROJECT_DIR$/src/app/layout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/layout.tsx" afterDir="false" />
|
||||
- <change beforePath="$PROJECT_DIR$/src/app/page.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/page.tsx" afterDir="false" />
|
||||
- <change beforePath="$PROJECT_DIR$/tailwind.config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/tailwind.config.ts" afterDir="false" />
|
||||
- </list>
|
||||
+ <list default="true" id="42d2b9e8-8d19-46be-8636-69ddba4519e4" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
@@ -27,6 +20,10 @@
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
+ <option name="UPDATE_TYPE" value="REBASE" />
|
||||
+ </component>
|
||||
+ <component name="PackageJsonUpdateNotifier">
|
||||
+ <dismissed value="$PROJECT_DIR$/package.json" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 2
|
||||
@@ -83,10 +80,33 @@
|
||||
<updated>1715776265663</updated>
|
||||
<workItem from="1715776267209" duration="3260000" />
|
||||
<workItem from="1715781823049" duration="3317000" />
|
||||
+ <workItem from="1715785249624" duration="857000" />
|
||||
</task>
|
||||
+ <task id="LOCAL-00001" summary="Add generated system configs, theme and page elements This commit adds the necessary configuration files for the system, including components for the Tailwind CSS and version control. It also includes code for creating a basic user interface with theme and page elements. Additionally, the commit includes new SVG resources for design elements.">
|
||||
+ <option name="closed" value="true" />
|
||||
+ <created>1715785408587</created>
|
||||
+ <option name="number" value="00001" />
|
||||
+ <option name="presentableId" value="LOCAL-00001" />
|
||||
+ <option name="project" value="LOCAL" />
|
||||
+ <updated>1715785408587</updated>
|
||||
+ </task>
|
||||
+ <task id="LOCAL-00002" summary="Extended README with project details and instructions An elaborate explanation of the project, development setup, and deployment instructions has been added to the README. This includes details of the technologies utilized, step-by-step instructions for running the development server, and links to useful resources for further readme.">
|
||||
+ <option name="closed" value="true" />
|
||||
+ <created>1715785443613</created>
|
||||
+ <option name="number" value="00002" />
|
||||
+ <option name="presentableId" value="LOCAL-00002" />
|
||||
+ <option name="project" value="LOCAL" />
|
||||
+ <updated>1715785443613</updated>
|
||||
+ </task>
|
||||
+ <option name="localTasksCounter" value="3" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
+ <component name="VcsManagerConfiguration">
|
||||
+ <MESSAGE value="Add generated system configs, theme and page elements This commit adds the necessary configuration files for the system, including components for the Tailwind CSS and version control. It also includes code for creating a basic user interface with theme and page elements. Additionally, the commit includes new SVG resources for design elements." />
|
||||
+ <MESSAGE value="Extended README with project details and instructions An elaborate explanation of the project, development setup, and deployment instructions has been added to the README. This includes details of the technologies utilized, step-by-step instructions for running the development server, and links to useful resources for further readme." />
|
||||
+ <option name="LAST_COMMIT_MESSAGE" value="Extended README with project details and instructions An elaborate explanation of the project, development setup, and deployment instructions has been added to the README. This includes details of the technologies utilized, step-by-step instructions for running the development server, and links to useful resources for further readme." />
|
||||
+ </component>
|
||||
</project>
|
||||
\ No newline at end of file
|
4
.idea/shelf/Uncommitted_changes_before_Update_at_5_16_24__8_59_AM__Changes_.xml
generated
Normal file
4
.idea/shelf/Uncommitted_changes_before_Update_at_5_16_24__8_59_AM__Changes_.xml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<changelist name="Uncommitted_changes_before_Update_at_5_16_24,_8_59_AM_[Changes]" date="1715842838887" recycled="true" deleted="true">
|
||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Update_at_5_16_24,_8_59_AM_[Changes]/shelved.patch" />
|
||||
<option name="DESCRIPTION" value="Uncommitted changes before Update at 5/16/24, 8:59 AM [Changes]" />
|
||||
</changelist>
|
19
.idea/workspace.xml
generated
19
.idea/workspace.xml
generated
@ -5,17 +5,16 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="42d2b9e8-8d19-46be-8636-69ddba4519e4" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/src/app/account/login/page.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/app/account/register/page.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/components/login-form.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/components/register-form.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/app/threads/page.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/components/legals.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-lock.yaml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/app/layout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/layout.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/app/page.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/page.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/header.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/header.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/theme-selector.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/theme-selector.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/login-form.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/login-form.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/register-form.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/register-form.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/ui/toast.tsx" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/ui/toaster.tsx" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/ui/use-toast.ts" beforeDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@ -25,8 +24,8 @@
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="TypeScript File" />
|
||||
<option value="TypeScript JSX File" />
|
||||
<option value="TypeScript File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
@ -57,7 +56,7 @@
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"npm.dev.executor": "Run",
|
||||
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||
"ts.external.directory.path": "/home/avnyr/monoliths/Simplon/brief-06-front/node_modules/typescript/lib",
|
||||
"ts.external.directory.path": "/home/avnyr/workspace/Simplon/brief-06-front/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
|
2229
pnpm-lock.yaml
generated
2229
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -2,15 +2,20 @@ import type { Metadata } from "next";
|
||||
import '@fontsource-variable/kode-mono';
|
||||
import "./globals.css";
|
||||
import {ThemeProvider} from "@/components/theme-provider";
|
||||
import {ToastProvider} from "@/components/ui/toast";
|
||||
import Header from "@/components/header";
|
||||
import {BackgroundBeams} from "@/components/ui/background-beams";
|
||||
import React from "react";
|
||||
import Legals from "@/components/legals";
|
||||
import {Toaster} from "react-hot-toast";
|
||||
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "OnlyDevs",
|
||||
description: "A social network for nerdy developers",
|
||||
authors: {
|
||||
name: "Yidhra Studio",
|
||||
url: "https://yidhra.fr"
|
||||
}
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@ -20,20 +25,21 @@ export default function RootLayout({
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={"flex flex-col justify-center items-center relative"}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
//disableTransitionOnChange
|
||||
>
|
||||
<Header/>
|
||||
{children}
|
||||
<ToastProvider/>
|
||||
</ThemeProvider>
|
||||
<BackgroundBeams className={"-z-10 w-full h-full fixed"}/>
|
||||
</body>
|
||||
</html>
|
||||
<body className={"flex flex-col justify-between items-center relative w-screen min-h-screen"}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
//disableTransitionOnChange
|
||||
>
|
||||
<Header/>
|
||||
{children}
|
||||
<Toaster/>
|
||||
<Legals/>
|
||||
</ThemeProvider>
|
||||
<BackgroundBeams className={"-z-10 w-full h-full fixed"}/>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
;
|
||||
}
|
||||
|
16
src/app/threads/page.tsx
Normal file
16
src/app/threads/page.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import {OctagonX} from "lucide-react";
|
||||
|
||||
export default function ThreadsPage() {
|
||||
return (
|
||||
<main className="flex flex-col justify-start items-center h-full w-full">
|
||||
<div className={"flex flex-row items-center gap-2 scale-90 md:scale-100 p-2 rounded border bg-accent"}>
|
||||
<OctagonX/>
|
||||
<div className={"flex flex-col justify-center items-start"}>
|
||||
<h3 className={"text-nowrap font-bold text-xl"}>Oups !</h3>
|
||||
<p className={"text-wrap"}>Something went wrong...</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
15
src/components/legals.tsx
Normal file
15
src/components/legals.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {ThemeSelector} from "@/components/theme-selector";
|
||||
import Link from "next/link";
|
||||
|
||||
export const Legals = () => {
|
||||
return (
|
||||
<footer className="flex flex-row justify-between items-center p-2 w-full self-end">
|
||||
<div className="flex items-center justify-between">
|
||||
Lorem ipsum dolor
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
export default Legals;
|
@ -4,6 +4,8 @@ import {Label} from "@/components/ui/label";
|
||||
import {Input} from "@/components/ui/input";
|
||||
import {cn} from "@/lib/utils";
|
||||
import {User} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import {Button} from "@/components/ui/button";
|
||||
|
||||
export function LoginForm() {
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
@ -11,26 +13,12 @@ export function LoginForm() {
|
||||
console.log("Form submitted");
|
||||
};
|
||||
return (
|
||||
<div className="max-w-md w-full mx-auto rounded-none md:rounded-2xl p-4 md:p-8 shadow-input">
|
||||
<div className="max-w-md w-full mx-auto rounded-none md:rounded-2xl p-4 md:p-8 shadow-input md:scale-125">
|
||||
<h2 className="font-bold text-xl text-neutral-800 dark:text-neutral-200">
|
||||
Welcome to OnlyDevs
|
||||
Login to your account
|
||||
</h2>
|
||||
<p className="text-neutral-600 text-sm max-w-sm mt-2 dark:text-neutral-300">
|
||||
Login to aceternity if you can because we don't have a login flow
|
||||
yet
|
||||
</p>
|
||||
|
||||
<form className="my-8" onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col md:flex-row space-y-2 md:space-y-0 md:space-x-2 mb-4">
|
||||
<LabelInputContainer>
|
||||
<Label htmlFor="firstname">First name</Label>
|
||||
<Input id="firstname" placeholder="John" type="text" />
|
||||
</LabelInputContainer>
|
||||
<LabelInputContainer>
|
||||
<Label htmlFor="lastname">Last name</Label>
|
||||
<Input id="lastname" placeholder="Doe" type="text" />
|
||||
</LabelInputContainer>
|
||||
</div>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="email">Email Address</Label>
|
||||
<Input id="email" placeholder="projectmayhem@fc.com" type="email" />
|
||||
@ -39,36 +27,32 @@ export function LoginForm() {
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" placeholder="••••••••" type="password" />
|
||||
</LabelInputContainer>
|
||||
<LabelInputContainer className="mb-8">
|
||||
<Label htmlFor="twitterpassword">Your twitter password</Label>
|
||||
<Input
|
||||
id="twitterpassword"
|
||||
placeholder="••••••••"
|
||||
type="twitterpassword"
|
||||
/>
|
||||
</LabelInputContainer>
|
||||
|
||||
<button
|
||||
className="bg-gradient-to-br relative group/btn from-black dark:from-zinc-900 dark:to-zinc-900 to-neutral-600 block dark:bg-zinc-800 w-full text-white rounded-md h-10 font-medium shadow-[0px_1px_0px_0px_#ffffff40_inset,0px_-1px_0px_0px_#ffffff40_inset] dark:shadow-[0px_1px_0px_0px_var(--zinc-800)_inset,0px_-1px_0px_0px_var(--zinc-800)_inset]"
|
||||
type="submit"
|
||||
>
|
||||
Sign up →
|
||||
Login →
|
||||
<BottomGradient />
|
||||
</button>
|
||||
|
||||
<div className="bg-gradient-to-r from-transparent via-neutral-300 dark:via-neutral-700 to-transparent my-8 h-[1px] w-full" />
|
||||
|
||||
<div className="flex flex-col space-y-4">
|
||||
<button
|
||||
className=" relative group/btn flex space-x-2 items-center justify-start px-4 w-full text-black rounded-md h-10 font-medium shadow-input bg-gray-50 dark:bg-zinc-900 dark:shadow-[0px_0px_1px_1px_var(--neutral-800)]"
|
||||
type="submit"
|
||||
<Button
|
||||
className=" relative group/btn flex space-x-2 items-center justify-center px-4 w-full text-black rounded-md h-10 font-medium shadow-input bg-gray-50 dark:bg-zinc-900 dark:shadow-[0px_0px_1px_1px_var(--neutral-800)]"
|
||||
asChild
|
||||
>
|
||||
<User className="h-4 w-4 text-neutral-800 dark:text-neutral-300" />
|
||||
<span className="text-neutral-700 dark:text-neutral-300 text-sm">
|
||||
OnlyFans
|
||||
</span>
|
||||
<BottomGradient />
|
||||
</button>
|
||||
<Link
|
||||
className={'flex flex-row justify-center items-center gap-2'}
|
||||
href={"/account/register"}>
|
||||
<User className="h-4 w-4 text-neutral-800 dark:text-neutral-300"/>
|
||||
<span className="text-neutral-700 dark:text-neutral-300 text-sm">
|
||||
I don't have an account
|
||||
</span>
|
||||
<BottomGradient/>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -78,8 +62,8 @@ export function LoginForm() {
|
||||
const BottomGradient = () => {
|
||||
return (
|
||||
<>
|
||||
<span className="group-hover/btn:opacity-100 block transition duration-500 opacity-0 absolute h-px w-full -bottom-px inset-x-0 bg-gradient-to-r from-transparent via-cyan-500 to-transparent" />
|
||||
<span className="group-hover/btn:opacity-100 blur-sm block transition duration-500 opacity-0 absolute h-px w-1/2 mx-auto -bottom-px inset-x-10 bg-gradient-to-r from-transparent via-indigo-500 to-transparent" />
|
||||
<span className="group-hover/btn:opacity-100 block transition duration-500 opacity-0 absolute h-px w-full -bottom-px inset-x-0 bg-gradient-to-r from-transparent via-accent-foreground to-transparent" />
|
||||
<span className="group-hover/btn:opacity-100 blur-sm block transition duration-500 opacity-0 absolute h-px w-1/2 mx-auto -bottom-px inset-x-10 bg-gradient-to-r from-transparent via-primary-foreground to-transparent" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -3,15 +3,85 @@ import React from "react";
|
||||
import {Label} from "@/components/ui/label";
|
||||
import {Input} from "@/components/ui/input";
|
||||
import {cn} from "@/lib/utils";
|
||||
import {User} from "lucide-react";
|
||||
import {OctagonX, User} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Resolver, useForm} from "react-hook-form";
|
||||
import toast, {ToastBar} from "react-hot-toast";
|
||||
import {ToastBox, toastType} from "@/components/ui/toast-box";
|
||||
|
||||
interface UserInsert {
|
||||
username: string,
|
||||
displayName?: string,
|
||||
email: string,
|
||||
password: string,
|
||||
rePassword: string
|
||||
error?: Record<string, { type: string; message: string }>
|
||||
}
|
||||
|
||||
export const resolver: Resolver<UserInsert> = async (values) => {
|
||||
const errors: Record<string, { type: string; message: string }> = {}
|
||||
|
||||
if (!values.username) {
|
||||
errors.name = {
|
||||
type: 'required',
|
||||
message: 'The username is required.',
|
||||
}
|
||||
}
|
||||
|
||||
if (!values.displayName) {
|
||||
errors.description = {
|
||||
type: 'required',
|
||||
message: 'The displayname is required.',
|
||||
}
|
||||
}
|
||||
|
||||
if (!values.email) {
|
||||
errors.start = {
|
||||
type: 'required',
|
||||
message: 'The email is required.',
|
||||
}
|
||||
}
|
||||
|
||||
if (!values.password) {
|
||||
errors.promoId = {
|
||||
type: 'required',
|
||||
message: 'The password is required.',
|
||||
}
|
||||
}
|
||||
|
||||
if (!values.rePassword) {
|
||||
errors.promoId = {
|
||||
type: 'required',
|
||||
message: 'You should retype the password.',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
values: Object.keys(errors).length === 0 ? values : {},
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
export function RegisterForm() {
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleSubbmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
console.log("Form submitted");
|
||||
};
|
||||
|
||||
const { register, handleSubmit, getValues } = useForm();
|
||||
|
||||
const onSubmit = (data: UserInsert) => {
|
||||
console.log(data);
|
||||
if (!data.error) {
|
||||
toast.custom(<ToastBox
|
||||
message={'Hello world !'}
|
||||
type={toastType.success}
|
||||
/>)
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="max-w-md w-full mx-auto rounded-none md:rounded-2xl p-4 md:p-8 shadow-input">
|
||||
<div className="max-w-md w-full mx-auto rounded-none md:rounded-2xl p-4 md:p-8 shadow-input md:scale-125">
|
||||
<h2 className="font-bold text-xl text-neutral-800 dark:text-neutral-200">
|
||||
Create an account
|
||||
</h2>
|
||||
@ -19,41 +89,54 @@ export function RegisterForm() {
|
||||
By creating an account you can discuss on our platform.
|
||||
</p>
|
||||
|
||||
<form className="my-8" onSubmit={handleSubmit}>
|
||||
<form className="my-8" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col md:flex-row space-y-2 md:space-y-0 md:space-x-2 mb-4">
|
||||
<LabelInputContainer>
|
||||
<Label htmlFor="firstname">First name</Label>
|
||||
<Input id="firstname" placeholder="John" type="text"/>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="username">Username<em className={"opacity-50"}>*</em></Label>
|
||||
<Input
|
||||
id="username"
|
||||
placeholder="john.doe68"
|
||||
type="text"
|
||||
{...register("username", { required: true })}
|
||||
/>
|
||||
</LabelInputContainer>
|
||||
<LabelInputContainer>
|
||||
<Label htmlFor="lastname">Last name</Label>
|
||||
<Input id="lastname" placeholder="Doe" type="text"/>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="displayName">Displayname</Label>
|
||||
<Input
|
||||
id="displayName"
|
||||
placeholder="Johny"
|
||||
type="text"
|
||||
{...register("displayName")}
|
||||
/>
|
||||
</LabelInputContainer>
|
||||
</div>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="email">Email Address</Label>
|
||||
<Input id="email" placeholder="john.doe@foo.bar" type="email"/>
|
||||
</LabelInputContainer>
|
||||
<div
|
||||
className="bg-gradient-to-r from-transparent via-neutral-300 dark:via-neutral-700 to-transparent my-8 h-[1px] w-full"/>
|
||||
className="bg-gradient-to-r from-transparent via-neutral-300 dark:via-neutral-700 to-transparent my-4 h-[1px] w-full"/>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Input id="username" placeholder="john.doe68" type="text"/>
|
||||
<Label htmlFor="email">Email Address<em className={"opacity-50"}>*</em></Label>
|
||||
<Input
|
||||
id="email"
|
||||
placeholder="john.doe@foo.bar"
|
||||
type="email"
|
||||
{...register("email", { required: true })}
|
||||
/>
|
||||
</LabelInputContainer>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="displayName">Displayname</Label>
|
||||
<Input id="displayName" placeholder="Johny" type="text"/>
|
||||
</LabelInputContainer>
|
||||
<LabelInputContainer className="mb-4">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" placeholder="••••••••" type="password"/>
|
||||
<Label htmlFor="password">Password<em className={"opacity-50"}>*</em></Label>
|
||||
<Input
|
||||
id="password"
|
||||
placeholder="••••••••"
|
||||
type="password"
|
||||
{...register("password", { required: true })}
|
||||
/>
|
||||
</LabelInputContainer>
|
||||
<LabelInputContainer className="mb-8">
|
||||
<Label htmlFor="re-password">Re-type password</Label>
|
||||
<Label htmlFor="re-password">Re-type password<em className={"opacity-50"}>*</em></Label>
|
||||
<Input
|
||||
id="re-password"
|
||||
placeholder="••••••••"
|
||||
type="password"
|
||||
{...register("re-password", { validate: (value) => value === getValues('password') || "The passwords do not match" })}
|
||||
/>
|
||||
</LabelInputContainer>
|
||||
|
||||
@ -64,6 +147,25 @@ export function RegisterForm() {
|
||||
Register →
|
||||
<BottomGradient/>
|
||||
</button>
|
||||
<div
|
||||
className="bg-gradient-to-r from-transparent via-neutral-300 dark:via-neutral-700 to-transparent my-8 h-[1px] w-full"/>
|
||||
|
||||
<div className="flex flex-col space-y-4">
|
||||
<Button
|
||||
className=" relative group/btn flex space-x-2 items-center justify-center px-4 w-full text-black rounded-md h-10 font-medium shadow-input bg-gray-50 dark:bg-zinc-900 dark:shadow-[0px_0px_1px_1px_var(--neutral-800)]"
|
||||
asChild
|
||||
>
|
||||
<Link
|
||||
className={'flex flex-row justify-center items-center gap-2'}
|
||||
href={"/account/login"}>
|
||||
<User className="h-4 w-4 text-neutral-800 dark:text-neutral-300"/>
|
||||
<span className="text-neutral-700 dark:text-neutral-300 text-sm">
|
||||
I already have an account
|
||||
</span>
|
||||
<BottomGradient/>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
@ -72,9 +174,8 @@ export function RegisterForm() {
|
||||
const BottomGradient = () => {
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className="group-hover/btn:opacity-100 block transition duration-500 opacity-0 absolute h-px w-full -bottom-px inset-x-0 bg-gradient-to-r from-transparent via-cyan-500 to-transparent" />
|
||||
<span className="group-hover/btn:opacity-100 blur-sm block transition duration-500 opacity-0 absolute h-px w-1/2 mx-auto -bottom-px inset-x-10 bg-gradient-to-r from-transparent via-indigo-500 to-transparent" />
|
||||
<span className="group-hover/btn:opacity-100 block transition duration-500 opacity-0 absolute h-px w-full -bottom-px inset-x-0 bg-gradient-to-r from-transparent via-accent-foreground to-transparent" />
|
||||
<span className="group-hover/btn:opacity-100 blur-sm block transition duration-500 opacity-0 absolute h-px w-1/2 mx-auto -bottom-px inset-x-10 bg-gradient-to-r from-transparent via-primary-foreground to-transparent" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
63
src/components/ui/toast-box.tsx
Normal file
63
src/components/ui/toast-box.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import {CircleCheckBig, MessageSquareText, OctagonX} from "lucide-react";
|
||||
import React from "react";
|
||||
|
||||
|
||||
export enum toastType {
|
||||
info = "info",
|
||||
warn = "warn",
|
||||
error = "error",
|
||||
refused= "refused",
|
||||
success = "success",
|
||||
add = "add",
|
||||
del = "del",
|
||||
message = "message"
|
||||
}
|
||||
|
||||
export function ToastBox({title, message, type}: {title?: string, message: string, type: toastType}) {
|
||||
let icon: any;
|
||||
let bgColor: string;
|
||||
switch (type) {
|
||||
case toastType.message :
|
||||
icon = <MessageSquareText />
|
||||
bgColor = 'bg-accent'
|
||||
break
|
||||
case toastType.add:
|
||||
icon = '<Plus />'
|
||||
bgColor = 'bg-accent'
|
||||
break
|
||||
case toastType.del:
|
||||
icon = '<Minus />'
|
||||
bgColor = 'bg-accent'
|
||||
break
|
||||
case toastType.info:
|
||||
icon = '<CircleHelp />'
|
||||
bgColor = 'bg-accent'
|
||||
break
|
||||
case toastType.warn:
|
||||
icon = '<CircleAlert />'
|
||||
bgColor = 'bg-orange-500'
|
||||
break
|
||||
case toastType.error:
|
||||
icon = '<Bug />'
|
||||
bgColor = 'bg-red-500'
|
||||
break
|
||||
case toastType.success:
|
||||
icon = <CircleCheckBig />
|
||||
bgColor = 'bg-green-500'
|
||||
break
|
||||
case toastType.refused:
|
||||
icon = '<OctagonX />'
|
||||
bgColor = 'bg-red-700'
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`flex flex-row items-center gap-2 scale-90 md:scale-100 p-2 rounded border ${bgColor}`}>
|
||||
{icon}
|
||||
<div className={"flex flex-col justify-center items-start"}>
|
||||
{title && <h3 className={"text-nowrap font-bold text-xl"}>{title}</h3>}
|
||||
<p className={"text-wrap"}>{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ToastPrimitives from "@radix-ui/react-toast"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const ToastProvider = ToastPrimitives.Provider
|
||||
|
||||
const ToastViewport = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Viewport
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||
|
||||
const toastVariants = cva(
|
||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "border bg-background text-foreground",
|
||||
destructive:
|
||||
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Toast = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
||||
VariantProps<typeof toastVariants>
|
||||
>(({ className, variant, ...props }, ref) => {
|
||||
return (
|
||||
<ToastPrimitives.Root
|
||||
ref={ref}
|
||||
className={cn(toastVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Toast.displayName = ToastPrimitives.Root.displayName
|
||||
|
||||
const ToastAction = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Action>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Action
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastAction.displayName = ToastPrimitives.Action.displayName
|
||||
|
||||
const ToastClose = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Close>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Close
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
||||
className
|
||||
)}
|
||||
toast-close=""
|
||||
{...props}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</ToastPrimitives.Close>
|
||||
))
|
||||
ToastClose.displayName = ToastPrimitives.Close.displayName
|
||||
|
||||
const ToastTitle = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Title
|
||||
ref={ref}
|
||||
className={cn("text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
||||
|
||||
const ToastDescription = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm opacity-90", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
||||
|
||||
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
||||
|
||||
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
||||
|
||||
export {
|
||||
type ToastProps,
|
||||
type ToastActionElement,
|
||||
ToastProvider,
|
||||
ToastViewport,
|
||||
Toast,
|
||||
ToastTitle,
|
||||
ToastDescription,
|
||||
ToastClose,
|
||||
ToastAction,
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
Toast,
|
||||
ToastClose,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
} from "@/components/ui/toast"
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
|
||||
export function Toaster() {
|
||||
const { toasts } = useToast()
|
||||
|
||||
return (
|
||||
<ToastProvider>
|
||||
{toasts.map(function ({ id, title, description, action, ...props }) {
|
||||
return (
|
||||
<Toast key={id} {...props}>
|
||||
<div className="grid gap-1">
|
||||
{title && <ToastTitle>{title}</ToastTitle>}
|
||||
{description && (
|
||||
<ToastDescription>{description}</ToastDescription>
|
||||
)}
|
||||
</div>
|
||||
{action}
|
||||
<ToastClose />
|
||||
</Toast>
|
||||
)
|
||||
})}
|
||||
<ToastViewport />
|
||||
</ToastProvider>
|
||||
)
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
"use client"
|
||||
|
||||
// Inspired by react-hot-toast library
|
||||
import * as React from "react"
|
||||
|
||||
import type {
|
||||
ToastActionElement,
|
||||
ToastProps,
|
||||
} from "@/components/ui/toast"
|
||||
|
||||
const TOAST_LIMIT = 1
|
||||
const TOAST_REMOVE_DELAY = 1000000
|
||||
|
||||
type ToasterToast = ToastProps & {
|
||||
id: string
|
||||
title?: React.ReactNode
|
||||
description?: React.ReactNode
|
||||
action?: ToastActionElement
|
||||
}
|
||||
|
||||
const actionTypes = {
|
||||
ADD_TOAST: "ADD_TOAST",
|
||||
UPDATE_TOAST: "UPDATE_TOAST",
|
||||
DISMISS_TOAST: "DISMISS_TOAST",
|
||||
REMOVE_TOAST: "REMOVE_TOAST",
|
||||
} as const
|
||||
|
||||
let count = 0
|
||||
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
type ActionType = typeof actionTypes
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: ActionType["ADD_TOAST"]
|
||||
toast: ToasterToast
|
||||
}
|
||||
| {
|
||||
type: ActionType["UPDATE_TOAST"]
|
||||
toast: Partial<ToasterToast>
|
||||
}
|
||||
| {
|
||||
type: ActionType["DISMISS_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
| {
|
||||
type: ActionType["REMOVE_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
|
||||
interface State {
|
||||
toasts: ToasterToast[]
|
||||
}
|
||||
|
||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||
|
||||
const addToRemoveQueue = (toastId: string) => {
|
||||
if (toastTimeouts.has(toastId)) {
|
||||
return
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(toastId)
|
||||
dispatch({
|
||||
type: "REMOVE_TOAST",
|
||||
toastId: toastId,
|
||||
})
|
||||
}, TOAST_REMOVE_DELAY)
|
||||
|
||||
toastTimeouts.set(toastId, timeout)
|
||||
}
|
||||
|
||||
export const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "ADD_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||
}
|
||||
|
||||
case "UPDATE_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||
),
|
||||
}
|
||||
|
||||
case "DISMISS_TOAST": {
|
||||
const { toastId } = action
|
||||
|
||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||
// but I'll keep it here for simplicity
|
||||
if (toastId) {
|
||||
addToRemoveQueue(toastId)
|
||||
} else {
|
||||
state.toasts.forEach((toast) => {
|
||||
addToRemoveQueue(toast.id)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === toastId || toastId === undefined
|
||||
? {
|
||||
...t,
|
||||
open: false,
|
||||
}
|
||||
: t
|
||||
),
|
||||
}
|
||||
}
|
||||
case "REMOVE_TOAST":
|
||||
if (action.toastId === undefined) {
|
||||
return {
|
||||
...state,
|
||||
toasts: [],
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const listeners: Array<(state: State) => void> = []
|
||||
|
||||
let memoryState: State = { toasts: [] }
|
||||
|
||||
function dispatch(action: Action) {
|
||||
memoryState = reducer(memoryState, action)
|
||||
listeners.forEach((listener) => {
|
||||
listener(memoryState)
|
||||
})
|
||||
}
|
||||
|
||||
type Toast = Omit<ToasterToast, "id">
|
||||
|
||||
function toast({ ...props }: Toast) {
|
||||
const id = genId()
|
||||
|
||||
const update = (props: ToasterToast) =>
|
||||
dispatch({
|
||||
type: "UPDATE_TOAST",
|
||||
toast: { ...props, id },
|
||||
})
|
||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
||||
|
||||
dispatch({
|
||||
type: "ADD_TOAST",
|
||||
toast: {
|
||||
...props,
|
||||
id,
|
||||
open: true,
|
||||
onOpenChange: (open) => {
|
||||
if (!open) dismiss()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
id: id,
|
||||
dismiss,
|
||||
update,
|
||||
}
|
||||
}
|
||||
|
||||
function useToast() {
|
||||
const [state, setState] = React.useState<State>(memoryState)
|
||||
|
||||
React.useEffect(() => {
|
||||
listeners.push(setState)
|
||||
return () => {
|
||||
const index = listeners.indexOf(setState)
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}, [state])
|
||||
|
||||
return {
|
||||
...state,
|
||||
toast,
|
||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||
}
|
||||
}
|
||||
|
||||
export { useToast, toast }
|
Loading…
x
Reference in New Issue
Block a user