Commit 6899078b by Arjun Jhukal

initial commit

parent a76cabfa
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import prettier from 'eslint-plugin-prettier';
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import js from '@eslint/js';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
...compat.extends('next/core-web-vitals', 'next/typescript', 'prettier'),
{
ignores: [
"node_modules/**",
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
],
ignores: ['**/node_modules/*', '**/.next/*']
},
{
plugins: {
prettier,
'@typescript-eslint': typescriptEslint
},
languageOptions: {
parser: tsParser,
ecmaVersion: 5,
sourceType: 'module',
parserOptions: {
project: './tsconfig.json',
createDefaultProgram: true
}
},
settings: {
'import/resolver': {
node: {
moduleDirectory: ['node_modules', 'src/']
},
typescript: {
alwaysTryTypes: true
}
}
},
rules: {
'react/jsx-filename-extension': 'off',
'no-param-reassign': 'off',
'react/prop-types': 'off',
'react/require-default-props': 'off',
'react/no-array-index-key': 'off',
'react/react-in-jsx-scope': 'off',
'react/jsx-props-no-spreading': 'off',
'import/order': 'off',
'no-console': 'off',
'no-shadow': 'off',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'import/no-cycle': 'off',
'prefer-destructuring': 'off',
'import/no-extraneous-dependencies': 'off',
'react/display-name': 'off',
'import/no-unresolved': ['off', { caseSensitive: false }],
'import/no-unresolved': [
'off',
{
caseSensitive: false
}
],
'no-restricted-imports': [
'error',
{
patterns: ['@mui/*/*/*', '!@mui/material/test-utils/*']
}
],
'@typescript-eslint/no-unused-vars': [
'error',
{
vars: 'all',
args: 'none'
}
],
'prettier/prettier': [
'warn',
{
bracketSpacing: true,
printWidth: 140,
singleQuote: true,
trailingComma: 'none',
tabWidth: 2,
useTabs: false,
endOfLine: "lf"
}
]
}
}
];
export default eslintConfig;
......@@ -9,19 +9,27 @@
"lint": "eslint"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/material": "^7.3.2",
"@reduxjs/toolkit": "^2.9.0",
"formik": "^2.4.6",
"next": "15.5.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.5.3"
"react-redux": "^9.2.0",
"yup": "^1.7.0",
"@wandersonalwes/iconsax-react": "0.0.10"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.5.3",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4",
"typescript": "^5"
}
}
\ No newline at end of file
import Box from '@mui/material/Box'
import React from 'react'
export default function AuthRootLayout({ children }: { children: React.ReactNode }) {
return (
<Box className="flex items-center min-h-screen justify-center">
{children}
</Box>
)
}
import LoginPage from '@/components/pages/auth/login'
import React from 'react'
export default function Login() {
return (
<LoginPage />
)
}
import RegisterPage from '@/components/pages/auth/register'
import React from 'react'
export default function Register() {
return (
<RegisterPage />
)
}
import { Box, Button, Typography } from '@mui/material'
import Image from 'next/image'
import Link from 'next/link'
import React from 'react'
export default function VerifyEmail() {
return (
<Box className="max-w-[520px] mx-auto flex flex-col gap-3 items-center text-center">
<Image src={"/assets/images/verify-email.png"} alt='' width={180} height={140} />
<h1 className='text-[24px] lg:text-[32px] leading-[120%] font-bold mb-4'>Verify your email to
get the fun started</h1>
{/* <Typography variant="h1" className='font-[700]'></Typography> */}
<p className='text-[14px] leading-[120%] font-normal lg:text-[16px] mb-4'>Check the link sent to <strong className='underline text-secondary'>abc@gmail.com</strong> to activate your account.</p>
<Button fullWidth size="large" type="submit" variant="contained" color="primary" className='!mb-6'>
Verify now
</Button>
<h2 className='text-[20px] lg:text-[28px] leading-[120%] font-bold'>Don’t see the email?</h2>
<p className='text-[11px] lg:text-[14px] leading-[150%] font-normal'>Please check <strong>your spam or junk folder,</strong> as the email may have been filtered there. Also, ensure that the email address you entered is correct. Still not found?</p>
<Button fullWidth variant="contained" color="secondary">
Send Again
</Button>
</Box>
)
}
import DashboardLayout from '@/components/layouts/DashboardLayout'
import Private from '@/routes/Private'
import React from 'react'
export default function DashboardRootLayout({ children }: { children: React.ReactNode }) {
return (
<Private>
<DashboardLayout>
{children}
</DashboardLayout>
</Private>
)
}
import Image from "next/image";
export default function Home() {
return (
<div className="dashboard__root">
<h1>Dashboard Root</h1>
</div>
);
}
import Toast from '@/components/molecules/Toast'
import { ThemeContextProvider } from '@/context/ThemeContext'
import { ClientProvider } from '@/hooks/ReduxProvider'
import ThemeCustomization from '@/theme'
import React from 'react'
export default function ProviderWrapper({ children }: { children: React.ReactNode }) {
return (
<ClientProvider>
<ThemeContextProvider>
<ThemeCustomization>
{children}
<Toast />
</ThemeCustomization>
</ThemeContextProvider>
</ClientProvider>
)
}
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
--white: #FFF;
--black: #000;
--gray: #E0E0E3;
--light-gray: #F3F4F6;
--primary-light: #71717A;
--primary: #B801C0;
--primary-dark: #3A013F;
--title: #0E0E11;
/* Additional variables for dark mode */
--primary-grad: linear-gradient(90deg, #B801C0 0%, #E046DC 100%);
--secondary-grad: linear-gradient(90deg, #69A29D 0%, #93E0D9 100%);
--secondary: #93E0D8;
--text-regular: rgba(0, 0, 0, 0.80);
--text-title: rgba(0, 0, 0, 0.90);
--gray-scale: #7E7181;
}
/* === Dark Theme === */
.dark {
--white: #000;
--black: #FFF;
--gray: #2D2D30;
--light-gray: #1F1F23;
--primary-light: #A0A0A7;
--primary: #D958DF;
--primary-dark: #7D0182;
--title: #F0F0F0;
/* Dark mode specific variables */
--primary-grad: linear-gradient(90deg, #B100B8 0%, #F335ED 100%);
--secondary-grad: linear-gradient(90deg, #69A29D 0%, #93E0D9 100%);
--secondary: #93E0D8;
--text-regular: rgba(255, 255, 255, 0.80);
--text-title: rgba(255, 255, 255, 0.90);
--gray-scale: #7E7181;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
/* Map CSS vars to Tailwind theme tokens */
@theme {
--color-white: var(--white);
--color-black: var(--black);
--color-gray: var(--gray);
--color-light-gray: var(--light-gray);
--color-primary-light: var(--primary-light);
--color-primary: var(--primary);
--color-primary-dark: var(--primary-dark);
--color-text: var(--text);
--color-secondary: var(--secondary);
--color-text-regular: var(--text-regular);
--color-text-title: var(--text-title);
--color-gray-scale: var(--gray-scale);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
@layer base {
h1,
h2,
h3 {
@apply font-[700]
}
label {
color: rgba(255, 255, 255, 0.80);
font-family: "Hiragino Sans";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
display: block;
margin-bottom: 4px;
}
input {
display: block;
width: 100%;
border-radius: 4px;
padding: 12px 16px;
}
input.rounded {
border-radius: 27px !important;
border: 0.576px solid rgba(255, 255, 255, 0.04);
background: rgba(118, 107, 120, 0.55);
}
input.rounded::placeholder {
color: rgba(255, 255, 255, 0.20);
font-weight: 300;
@apply text-[11px] lg:text-[14px]
}
input.rounded:focus {
@apply outline-secondary
}
.error {
@apply text-[12px] leading-[120%] text-red-500 block mt-1;
}
button,
.ss-btn {
padding: 12px 24px;
border-radius: 27px;
text-align: center;
width: 100%;
display: block;
}
:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
@layer utilities {
.dark {
background-color: var(--primary);
}
.bg-primary-grad {
background: var(--primary-grad);
}
.bg-secondary-grad {
background: var(--secondary-grad);
}
}
\ No newline at end of file
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
import ProviderWrapper from "./ProviderWrapper";
export const metadata: Metadata = {
title: "Create Next App",
title: "Sweepstake",
description: "Generated by create next app",
};
......@@ -24,10 +15,10 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<body className="dark">
<ProviderWrapper>
{children}
</ProviderWrapper>
</body>
</html>
);
......
import Image from "next/image";
export default function Home() {
return (
<div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
src/app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org →
</a>
</footer>
</div>
);
}
"use client";
import { Box } from '@mui/material'
import React from 'react'
import Sidebar from '../organism/Sidebar'
import Header from '../organism/Header'
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Box sx={{ display: 'flex' }}>
<Header open={open} handleDrawerOpen={handleDrawerOpen} />
<Sidebar open={open} handleDrawerOpen={handleDrawerOpen} />
<div className="root_container">
<div className="content_box p-4 lg:pl-11 lg:pr-12 lg:py-8">{children}</div>
</div>
</Box>
)
}
"use client";
import { IconButton, InputAdornment, InputLabel, OutlinedInput } from "@mui/material";
import { Eye, EyeSlash } from "@wandersonalwes/iconsax-react";
import { useState } from "react";
interface PasswordFieldProps {
label: string;
name: string;
placeholder?: string;
value: string;
onChange: React.ChangeEventHandler<HTMLInputElement>;
onBlur: React.FocusEventHandler<HTMLInputElement>;
error?: string;
}
export default function PasswordField({
label,
name,
placeholder,
value,
onChange,
onBlur,
error,
}: PasswordFieldProps) {
const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword((prev) => !prev);
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => event.preventDefault();
return (
<div className="input_field">
<InputLabel htmlFor={name}>{label}</InputLabel>
<OutlinedInput
id={name}
name={name}
type={showPassword ? 'text' : 'password'}
placeholder={placeholder}
value={value}
onChange={onChange}
onBlur={onBlur}
endAdornment={
< InputAdornment position="end" >
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
color="secondary"
>
{showPassword ? <Eye /> : <EyeSlash />}
</IconButton>
</InputAdornment >
}
/>
{error ? <span className="error">{error}</span> : null}
</div>
);
}
"use client";
import React from "react";
import { closeToast } from "@/slice/toastSlice";
import { useAppDispatch, useAppSelector } from "@/hooks/hook";
import { CloseCircle } from "@wandersonalwes/iconsax-react";
export default function Toast() {
const { variant, message, isActive, autoTimeout, duration } = useAppSelector(
(state) => state.toastSlice
);
const dispatch = useAppDispatch();
React.useEffect(() => {
if (isActive && autoTimeout) {
const timeout = setTimeout(() => {
dispatch(closeToast());
}, duration || 3000);
return () => clearTimeout(timeout);
}
}, [isActive, duration, dispatch, autoTimeout]);
if (!isActive) return null;
const variantStyles: Record<string, string> = {
success: "border-green-500 bg-green-50 text-green-800",
error: "border-red-500 bg-red-50 text-red-800",
warning: "border-yellow-500 bg-yellow-50 text-yellow-800",
info: "border-blue-500 bg-blue-50 text-blue-800",
};
const currentVariant = variant?.toLowerCase() || "info";
return (
<div
className={`fixed top-4 right-4 flex w-full max-w-sm items-start gap-3 rounded-xl border-l-4 px-4 py-3 shadow-lg transition-all duration-300 animate-in slide-in-from-right data-[state=closed]:slide-out-to-right data-[state=closed]:fade-out
${variantStyles[currentVariant]}`}
>
<div className="flex flex-col flex-1">
{variant && (
<h4 className="text-sm font-semibold">
{variant.charAt(0).toUpperCase() + variant.slice(1).toLowerCase()}
</h4>
)}
{message && <p className="text-sm leading-snug">{message}</p>}
</div>
<button
type="button"
onClick={() => dispatch(closeToast())}
className="text-current hover:opacity-70 transition-opacity"
>
<CloseCircle size="32" color="#FF8A65" />
</button>
</div>
);
}
import { DRAWER_WIDTH } from '@/config';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
import { styled, useTheme, Theme, CSSObject } from '@mui/material/styles';
import React from 'react'
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme }) => ({
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
variants: [
{
props: ({ open }) => open,
style: {
marginLeft: DRAWER_WIDTH,
width: `calc(100% - ${DRAWER_WIDTH}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
},
],
}));
export default function Header({ open, handleDrawerOpen }: {
open: boolean,
handleDrawerOpen: () => void;
}) {
return (
<AppBar position="fixed" open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={[
{
marginRight: 5,
},
open && { display: 'none' },
]}
>
{/* <MenuIcon /> */}
</IconButton>
<Typography variant="h6" noWrap component="div">
Mini variant drawer
</Typography>
</Toolbar>
</AppBar>
)
}
import * as React from 'react';
import { styled, useTheme, Theme, CSSObject } from '@mui/material/styles';
import Box from '@mui/material/Box';
import MuiDrawer from '@mui/material/Drawer';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import List from '@mui/material/List';
import CssBaseline from '@mui/material/CssBaseline';
import Typography from '@mui/material/Typography';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
// import MenuIcon from '@mui/icons-material/Menu';
// import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
// import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
// import InboxIcon from '@mui/icons-material/MoveToInbox';
// import MailIcon from '@mui/icons-material/Mail';
const drawerWidth = 240;
const openedMixin = (theme: Theme): CSSObject => ({
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
overflowX: 'hidden',
});
const closedMixin = (theme: Theme): CSSObject => ({
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
overflowX: 'hidden',
width: `calc(${theme.spacing(7)} + 1px)`,
[theme.breakpoints.up('sm')]: {
width: `calc(${theme.spacing(8)} + 1px)`,
},
});
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
}));
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme }) => ({
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
variants: [
{
props: ({ open }) => open,
style: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
},
],
}));
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
({ theme }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
variants: [
{
props: ({ open }) => open,
style: {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
},
},
{
props: ({ open }) => !open,
style: {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
},
},
],
}),
);
export default function Sidebar({ open, handleDrawerOpen }: {
open: boolean,
handleDrawerOpen: () => void;
}) {
return (
<Drawer variant="permanent" open={open}>
<DrawerHeader>
{/* <IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton> */}
</DrawerHeader>
<Divider />
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem key={text} disablePadding sx={{ display: 'block' }}>
<ListItemButton
sx={[
{
minHeight: 48,
px: 2.5,
},
open
? {
justifyContent: 'initial',
}
: {
justifyContent: 'center',
},
]}
>
<ListItemIcon
sx={[
{
minWidth: 0,
justifyContent: 'center',
},
open
? {
mr: 3,
}
: {
mr: 'auto',
},
]}
>
{/* {index % 2 === 0 ? <InboxIcon /> : <MailIcon />} */}
</ListItemIcon>
<ListItemText
primary={text}
sx={[
open
? {
opacity: 1,
}
: {
opacity: 0,
},
]}
/>
</ListItemButton>
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem key={text} disablePadding sx={{ display: 'block' }}>
<ListItemButton
sx={[
{
minHeight: 48,
px: 2.5,
},
open
? {
justifyContent: 'initial',
}
: {
justifyContent: 'center',
},
]}
>
<ListItemIcon
sx={[
{
minWidth: 0,
justifyContent: 'center',
},
open
? {
mr: 3,
}
: {
mr: 'auto',
},
]}
>
{/* {index % 2 === 0 ? <InboxIcon /> : <MailIcon />} */}
</ListItemIcon>
<ListItemText
primary={text}
sx={[
open
? {
opacity: 1,
}
: {
opacity: 0,
},
]}
/>
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
import { ImageProps } from '@/types/config'
import { List, ListItem } from '@mui/material'
import Box from '@mui/material/Box'
import Image from 'next/image'
interface Props {
image?: ImageProps
title: string;
features?: string[];
}
export default function AuthMessageBlock(props: Props) {
const { image, title, features } = props;
return (
<Box className="auth-image-wrapper relative hidden lg:block w-[50%] min-h-screen pl-9 pr-15 pb-15 rounded-[8px] overflow-hidden" >
<Image src={"/assets/images/auth-image.png"} alt="Auth Image" fill className="object-cover max-w-[100%] h-auto" />
<Box className="absolute bottom-15 left-9 right-15 lg:max-w-[40%]">
<h2 className='mb-4 text-[20px] lg:text-[32px] leading-[150%] text-bold'>{title && title}</h2>
{
features?.length ?
<ul >
{
features.map((feature) => (
<li key={feature} className='text-12 leading-[146%]'>{feature}</li>
))
}
</ul>
: null
}
</Box>
</Box>
)
}
'use client';
import React from 'react'
import AuthMessageBlock from '../authMessageBlock'
import { Box, InputLabel, OutlinedInput } from '@mui/material'
import { useFormik } from 'formik';
import Link from 'next/link';
import * as Yup from 'yup';
import { PATH } from '@/routes/PATH';
import { useRouter } from 'next/navigation';
import { useAppDispatch } from '@/hooks/hook';
import { useLoginMutation } from '@/services/authApi';
import { showToast, ToastVariant } from '@/slice/toastSlice';
import { setTokens } from '@/slice/authSlice';
import PasswordField from '@/components/molecules/PasswordField';
const validationSchema = Yup.object().shape({
emailAddress: Yup.string()
.email('Must be a valid email')
.max(255)
.required('Email is required'),
password: Yup.string()
.required('Password is required')
.test(
'no-leading-trailing-whitespace',
'Password cannot start or end with spaces',
(value) => value === value?.trim()
)
.max(12, 'Password must be less than 10 characters'),
})
export default function LoginPage() {
const initialValues = {
emailAddress: "",
password: "",
}
const router = useRouter();
const dispatch = useAppDispatch();
const [loginUser, { isLoading }] = useLoginMutation();
const { handleSubmit, handleBlur, handleChange, errors, dirty, values, touched } = useFormik(
{
initialValues,
validationSchema,
onSubmit: async (values) => {
try {
const response = await loginUser({
email: values.emailAddress,
password: values.password
}).unwrap();
dispatch(
showToast({
message: "Logged in successfully",
variant: ToastVariant.SUCCESS,
autoTime: true,
}),
);
dispatch(
setTokens({
accessToken: response.data.access_token,
// refreshToken: response.data?.refresh,
user: response.data?.user,
}),
);
router.replace(PATH.AUTH.VERIFY_EMAIL.ROOT)
}
catch (e) {
dispatch(
showToast({
message: "Something went wrong. Try again later",
variant: ToastVariant.ERROR,
autoTime: true,
}),
);
}
}
}
)
return (
<>
<AuthMessageBlock
title='Ready to get started and win BIG?'
features={["Fun & Fair Gameplay", "Exciting Prizes", "Accessible Anytime, Anywhere", "Trusted & Secure"]}
/>
<Box className="auth__form__wrapper lg:w-[50%] p-8">
<div className="section__title mb-4 lg:mb-6">
<h1 className='text-[24px] leading-[120%] font-bold lg:text-[48px]'>Setup an account</h1>
</div>
<form action="" onSubmit={handleSubmit}>
<div className="grid md:grid-cols-2 gap-x-3 gap-y-5">
<div className="col-span-2">
<div className="input_field">
<InputLabel htmlFor="emailAddress">Email Address*</InputLabel>
<OutlinedInput
name='emailAddress'
id='emailAddress'
placeholder='example@example.com'
value={values.emailAddress}
onChange={handleChange}
onBlur={handleBlur}
error={Boolean(touched.emailAddress && errors.emailAddress)}
/>
{
touched.emailAddress && errors.emailAddress ?
<span className="error ">{errors.emailAddress}</span> : null
}
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="password"
label="Password*"
placeholder="XXXXXXX"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
error={touched.password ? errors.password : undefined}
/>
</div>
</div>
</div>
<div className="action__group text-center flex flex-col gap-4 mt-8">
<button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Logging In" : "Login"}</button>
<p className='text-[12px] leading-[120%] font-bold lg:text-[16px]'>Forgot password? <Link href={PATH.AUTH.RESET_PASSWORD.ROOT}>Reset Here</Link></p>
<p className='text-[12px] leading-[120%] font-bold lg:text-[16px]'>Don’t have an account yet?</p>
<Link href={PATH.AUTH.REGISTER.ROOT} className='ss-btn bg-secondary-grad'>Setup an account</Link>
</div>
</form>
</Box>
</>
)
}
'use client';
import React from 'react'
import AuthMessageBlock from '../authMessageBlock'
import { Box, InputLabel, OutlinedInput } from '@mui/material';
import * as Yup from 'yup';
import { useRouter } from 'next/navigation';
import { Formik, useFormik } from 'formik';
import Link from 'next/link';
import { PATH } from '@/routes/PATH';
import { useRegisterUserMutation } from '@/services/authApi';
import { useAppDispatch } from '@/hooks/hook';
import { showToast, ToastVariant } from '@/slice/toastSlice';
import PasswordField from '@/components/molecules/PasswordField';
const validationSchema = Yup.object().shape({
emailAddress: Yup.string()
.email('Must be a valid email')
.max(255)
.required('Email is required'),
displayName: Yup.string()
.required('Display Name is required')
.max(50, 'Display Name must be less than 50 characters'),
password: Yup.string()
.required('Password is required')
.test(
'no-leading-trailing-whitespace',
'Password cannot start or end with spaces',
(value) => value === value?.trim()
)
.max(16, 'Password must be less than 10 characters'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Passwords must match')
.required('Confirm Password is required'),
})
export default function RegisterPage() {
const router = useRouter();
const [registerUser, { isLoading }] = useRegisterUserMutation();
const dispatch = useAppDispatch();
const initialValues = {
emailAddress: "",
displayName: "",
password: "",
confirmPassword: "",
}
const { handleSubmit, handleBlur, handleChange, errors, dirty, values, touched } = useFormik(
{
initialValues,
validationSchema,
onSubmit: async (values) => {
try {
const response = await registerUser({
email: values.emailAddress,
username: values.displayName,
password: values.password,
password_confirmation: values.confirmPassword,
}).unwrap();
console.log("response", response)
dispatch(
showToast({
message: "User Registerd Successfully",
variant: ToastVariant.SUCCESS,
autoTime: true,
}),
);
router.replace(PATH.AUTH.LOGIN.ROOT)
}
catch (e) {
dispatch(
showToast({
message: "Unable to register user. Try again later",
variant: ToastVariant.ERROR,
autoTime: true,
}),
);
}
}
}
)
return (
<>
<AuthMessageBlock
title="Welcome Back. Ready to rock today?"
features={["Fun & Fair Gameplay", "Exciting Prizes", "Accessible Anytime, Anywhere", "Trusted & Secure"]}
/>
<Box className="auth__form__wrapper lg:w-[50%] p-8">
<div className="section__title mb-4 lg:mb-6">
<h1 className='text-[24px] leading-[120%] font-bold lg:text-[48px]'>Setup an account</h1>
</div>
<form action="" onSubmit={handleSubmit}>
<div className="grid md:grid-cols-2 gap-x-3 gap-y-5">
<div className="col-span-2">
<div className="input_field">
<InputLabel htmlFor="emailAddress">Email Address*</InputLabel>
<OutlinedInput
name='emailAddress'
id='emailAddress'
placeholder='example@example.com'
value={values.emailAddress}
onChange={handleChange}
onBlur={handleBlur}
error={Boolean(touched.emailAddress && errors.emailAddress)}
/>
{
touched.emailAddress && errors.emailAddress ?
<span className="error ">{errors.emailAddress}</span> : null
}
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<InputLabel htmlFor="displayName">Display Name*</InputLabel>
<OutlinedInput
name='displayName'
id='displayName'
placeholder='John doe'
value={values.displayName}
onChange={handleChange}
onBlur={handleBlur}
error={Boolean(touched.displayName && errors.displayName)}
/>
{
touched.displayName && errors.displayName ?
<span className="error ">{errors.displayName}</span> : null
}
</div>
</div>
<div className="col-span-1">
<div className="input_field">
<PasswordField
name="password"
label="Password*"
placeholder="XXXXXXX"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
error={touched.password ? errors.password : undefined}
/>
</div>
</div>
<div className="col-span-1">
<div className="input_field">
<PasswordField
name="confirmPassword"
label="Confirm Password*"
placeholder="XXXXXXX"
value={values.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
error={touched.confirmPassword ? errors.confirmPassword : undefined}
/>
</div>
</div>
</div>
<div className="action__group text-center flex flex-col gap-4 mt-8">
<button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Signing Up" : "Sign up"}</button>
<p className='text-[12px] leading-[120%] font-bold lg:text-[16px]'>Already Have an account?</p>
<Link href={PATH.AUTH.LOGIN.ROOT} className='ss-btn bg-secondary-grad'>Login</Link>
</div>
</form>
</Box>
</>
)
}
import { Inter } from 'next/font/google';
import { DefaultConfigProps } from './types/config';
export const DRAWER_WIDTH = 280;
export const MINI_DRAWER_WIDTH = 90;
export const HEADER_HEIGHT = 74;
const inter = Inter({
subsets: ['latin'],
fallback: ['sans-serif'],
weight: ['300', '400', '500', '700'],
adjustFontFallback: false
});
export enum ThemeMode {
LIGHT = 'light',
DARK = 'dark',
AUTO = 'auto'
}
export enum Gender {
MALE = 'Male',
FEMALE = 'Female'
}
const config: DefaultConfigProps = {
fontFamily: inter.style.fontFamily,
i18n: 'en',
mode: ThemeMode.LIGHT,
miniDrawer: false
}
export default config;
\ No newline at end of file
"use client";
import config, { ThemeMode } from "@/config";
import useLocalStorage from "@/hooks/useLocalStorage";
import { CustomizationProps, I18n } from "@/types/config";
import React, { createContext, use } from "react";
const initialState: CustomizationProps = {
...config,
onChangeMode: () => { },
onChangeLocalization: () => { },
onChangeMiniDrawer: () => { },
}
const ThemeContext = createContext(initialState)
export const ThemeContextProvider = ({ children }: { children: React.ReactNode }) => {
const [config, setConfig] = useLocalStorage('sweepstake-config', initialState);
const onChangeLocalization = (lang: I18n) => {
setConfig({
...config,
i18n: lang
})
}
const onChangeMiniDrawer = (miniDrawer: boolean) => {
setConfig({
...config,
miniDrawer
})
}
const onChangeMode = (mode: ThemeMode) => {
setConfig({
...config,
mode
});
};
return (
<ThemeContext value={{ ...config, onChangeLocalization, onChangeMiniDrawer, onChangeMode }}>
{children}
</ThemeContext>
)
}
export const useThemeContext = () => {
return use(ThemeContext);
}
\ No newline at end of file
"use client";
import { Provider } from "react-redux";
import { store } from "@/hooks/store";
export function ClientProvider({ children }: { children: React.ReactNode }) {
return <Provider store={store}>{children}</Provider>;
}
import { useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();
import { authApi } from "@/services/authApi";
import { configureStore } from "@reduxjs/toolkit";
import auth from "@/slice/authSlice";
import toastSlice from "@/slice/toastSlice";
export const store = configureStore({
reducer: {
auth,
toastSlice,
[authApi.reducerPath]: authApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(authApi.middleware)
})
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
\ No newline at end of file
import React from 'react'
export default function useLocalStorage<ValueType>(key: string, defaultValue: ValueType) {
const [value, setValue] = React.useState(() => {
const storedValue = typeof window !== 'undefined' ? localStorage.getItem(key) : null;
return storedValue === null ? defaultValue : JSON.parse(storedValue);
});
React.useEffect(() => {
const listener = (e: StorageEvent) => {
if (typeof window !== 'undefined' && e.storageArea === localStorage && e.key === key) {
setValue(e.newValue ? JSON.parse(e.newValue) : e.newValue)
}
}
window.addEventListener('storage', listener);
return () => {
window.addEventListener('storage', listener);
}
}, [key, defaultValue])
const StoreValueInLocalStorage = (newValue: ValueType) => {
setValue((currentValue: any) => {
const result = typeof newValue === 'function' ? newValue(currentValue) : newValue;
if (typeof window !== 'undefined') localStorage.setItem(key, JSON.stringify(result));
return result;
})
}
return [value, StoreValueInLocalStorage]
}
export const PATH = {
DASHBOARD: {
ROOT: "/"
},
AUTH: {
LOGIN: {
ROOT: "/login"
},
REGISTER: {
ROOT: "/register"
},
RESET_PASSWORD: {
ROOT: "/reset-password"
},
VERIFY_EMAIL: {
ROOT: "/verify-email"
}
},
ADMIN: {
}
}
\ No newline at end of file
"use client";
import { useAppSelector } from "@/hooks/hook";
import auth from "@/slice/authSlice";
import { useRouter } from "next/navigation";
import React, { useEffect } from "react";
export default function Private({ children }: { children: React.ReactNode }) {
const router = useRouter();
const user = useAppSelector((state) => state.auth.user);
console.log(user);
useEffect(() => {
if (!user) {
router.replace("/login");
}
}, [user, router]);
if (!user) return null;
return <>{children}</>;
}
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery";
import { LoginProps, LoginResponse, RegisterProps } from "@/types/auth";
export const authApi = createApi({
reducerPath: "authApi",
baseQuery: baseQuery,
endpoints: (builder) => ({
registerUser: builder.mutation<{ success: boolean, data: LoginResponse | null, message: string }, RegisterProps>({
query: ({ email,
username,
password,
password_confirmation }) => ({
url: `/api/auth/register/`,
method: "POST",
body: {
email,
username,
password,
password_confirmation
},
})
}),
login: builder.mutation<LoginResponse, LoginProps>({
query: ({ email, password }) => ({
url: `/api/auth/login/`,
method: "POST",
body: { email, password },
})
})
})
})
export const { useLoginMutation, useRegisterUserMutation } = authApi;
\ No newline at end of file
import { RootState } from "@/hooks/store";
import { fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const baseQuery = fetchBaseQuery({
baseUrl: process.env.NEXT_PUBLIC_BASE_URL,
prepareHeaders(headers, { getState }) {
const token = (getState() as RootState).auth.access_token;
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
return headers;
},
});
import { RoleProps, User } from "@/types/auth";
import { createSlice } from "@reduxjs/toolkit";
type Data = {
access_token: string,
user: User | null,
}
const isBrowser = typeof window !== "undefined";
const tokens = isBrowser ? localStorage.getItem("token") : null;
let localStorageAccessToken = "";
let localStorageRefreshToken = "";
let localStorageUser = null;
if (tokens) {
try {
const parsedTokens = JSON.parse(tokens);
localStorageAccessToken = parsedTokens.accessToken || "";
localStorageRefreshToken = parsedTokens.refreshToken || "";
localStorageUser = parsedTokens.user || "";
} catch (error) {
console.error("Error parsing tokens from localStorage:", error);
}
}
const initialState: Data = {
// accessToken: localStorageAccessToken,
// refreshToken: localStorageRefreshToken,
access_token: localStorageAccessToken,
user: localStorageUser,
};
export const authSlice = createSlice({
name: "authSlice",
initialState,
reducers: {
setTokens: (state, action) => {
const { accessToken, refreshToken, user } = action.payload;
state.access_token = accessToken;
// state.refreshToken = refreshToken;
state.user = user;
localStorage.setItem(
"token",
JSON.stringify({ accessToken, refreshToken, user }),
);
if (isBrowser) {
localStorage.setItem(
"token",
JSON.stringify({ accessToken, refreshToken, user }),
);
}
},
clearTokens: (state) => {
state.access_token = "";
// state.refreshToken = "";
state.user = null;
if (isBrowser) {
localStorage.removeItem("token");
}
},
}
})
export const { setTokens, clearTokens } = authSlice.actions;
export default authSlice.reducer;
\ No newline at end of file
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export enum ToastVariant {
SUCCESS = "SUCCESS",
ERROR = "ERROR",
INFO = "INFO",
WARNING = "WARNING",
}
export interface ToastProps {
variant: ToastVariant | null;
message: string;
isActive: boolean;
autoTimeout?: boolean;
duration?: number;
}
const initialState: ToastProps = {
variant: null,
message: "",
isActive: false,
autoTimeout: false,
duration: 0,
};
const ToastSlice = createSlice({
name: "toast",
initialState,
reducers: {
showToast: (
state,
action: PayloadAction<{
variant: ToastVariant;
message: string;
autoTime?: boolean;
duration?: number;
}>,
) => {
const {
variant,
message,
autoTime = false,
duration = 3000,
} = action.payload;
state.variant = variant;
state.message = message;
state.isActive = true;
state.autoTimeout = autoTime;
state.duration = duration;
},
closeToast: (state) => {
state.isActive = false;
state.message = "";
state.variant = null;
state.autoTimeout = false;
state.duration = 0;
},
},
});
export const { showToast, closeToast } = ToastSlice.actions;
export default ToastSlice.reducer;
"use client";
import { CssBaseline, GlobalStyles } from '@mui/material'
import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles'
import React from 'react'
import Palette from './palette';
import { ThemeMode } from '@/config';
export default function ThemeCustomization({ children }: { children: React.ReactNode }) {
const globalStyles = {
':root': {
'--white': '#FFF',
'--black': '#000',
'--gray': '#E0E0E3',
'--light-gray': '#F3F4F6',
'--primary-light': '#71717A',
'--primary': '#B801C0',
'--primary-dark': '#3A013F',
'--text': '#0E0E11',
'--primary-grad': 'linear-gradient(90deg, #B801C0 0%, #E046DC 100%)',
'--secondary-grad': 'linear-gradient(90deg, #69A29D 0%, #93E0D9 100%)',
'--secondary': '#93E0D8',
'--text-regular': 'rgba(0, 0, 0, 0.80)',
'--text-title': 'rgba(0, 0, 0, 0.90)',
'--gray-scale': '#7E7181',
},
'.dark': {
'--white': '#000',
'--black': '#FFF',
'--gray': '#2D2D30',
'--light-gray': '#1F1F23',
'--primary-light': '#A0A0A7',
'--primary': '#D958DF',
'--primary-dark': '#7D0182',
'--text': '#F0F0F0',
'--primary-grad': 'linear-gradient(90deg, #B100B8 0%, #F335ED 100%)',
'--secondary-grad': 'linear-gradient(90deg, #69A29D 0%, #93E0D9 100%)',
'--secondary': '#93E0D8',
'--text-regular': 'rgba(255, 255, 255, 0.80)',
'--text-title': 'rgba(255, 255, 255, 0.90)',
'--gray-scale': '#7E7181',
},
};
const theme = Palette(ThemeMode.DARK);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<CssBaseline enableColorScheme />
<GlobalStyles styles={globalStyles}
/>
{children}
</ThemeProvider>
</StyledEngineProvider>
)
}
// src/theme/Palette.ts
import { createTheme, PaletteMode } from "@mui/material/styles";
import { ThemeMode } from "@/config";
import { PaletteThemeProps } from "@/types/theme";
export default function Palette(mode: ThemeMode) {
const contrastText = "#fff";
// Base Colors - Light Mode
let primaryColors = ['#71717A', '#B801C0', '#3A013F'];
let secondaryColors = ["#93E0D8", "#69A29D", "#4A7D78"]; // Added dark and light variants
let grayColors = ["#E0E0E3", '#F3F4F6'];
let titleColors = ["#0E0E11", "rgba(0, 0, 0, 0.80)", "rgba(0, 0, 0, 0.90)"];
let primaryGradColors = ['linear-gradient(90deg, #B100B8 0%, #F335ED 100%)'];
let secondaryGradColors = ['linear-gradient(90deg, #69A29D 0%, #93E0D9 100%)'];
if (mode === ThemeMode.DARK) {
primaryColors = ['#A0A0A7', '#D958DF', '#7D0182'];
secondaryColors = ["#93E0D8", "#69A29D", "#4A7D78"];
grayColors = ["#2D2D30", '#1F1F23'];
titleColors = ["#F0F0F0", "rgba(0, 0, 0, 0.80)", "rgba(0, 0, 0, 0.90)"];
}
const paletteColor: PaletteThemeProps = {
primary: {
light: primaryColors[0],
main: primaryColors[1],
dark: primaryColors[2],
contrastText
},
secondary: {
light: secondaryColors[0],
main: secondaryColors[0], // Use the same main color since we only have one base
dark: secondaryColors[0], // Use the same dark color
contrastText
},
title: {
main: titleColors[0],
contrastText
},
lightGray: {
light: grayColors[1],
main: grayColors[0],
contrastText
},
};
// Final MUI Theme
return createTheme({
palette: {
mode: mode as PaletteMode,
common: {
black: "#000",
white: "#fff",
},
...paletteColor,
background: {
default: mode === ThemeMode.DARK ? grayColors[1] : "#fff", // Fixed: use gray instead of secondary
paper: mode === ThemeMode.DARK ? grayColors[0] : "#fff" // Fixed: use gray instead of secondary
},
text: {
primary: mode === ThemeMode.DARK ? titleColors[0] : titleColors[0], // Fixed: use title colors
secondary: mode === ThemeMode.DARK ? grayColors[0] : grayColors[1] // Fixed: use gray colors
}
},
components: {
MuiInputLabel: {
styleOverrides: {
root: {
color: 'rgba(255, 255, 255, 0.80)',
fontFamily: '"Hiragino Sans"',
fontSize: '12px',
fontWeight: 400,
lineHeight: 'normal',
display: 'block',
marginBottom: '4px',
},
},
},
MuiOutlinedInput: {
styleOverrides: {
root: {
// display: "block",
width: "100%",
borderRadius: '27px',
background: 'rgba(118, 107, 120, 0.55)',
border: '0.576px solid rgba(255, 255, 255, 0.04)',
color: '#fff',
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: 'rgba(255,255,255,0.2)',
},
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: '#B801C0',
},
},
input: {
padding: '12px 16px',
'&::placeholder': {
color: 'rgba(255, 255, 255, 0.2)',
fontWeight: 300,
fontSize: '12px',
},
},
},
},
MuiButton: {
styleOverrides: {
root: {
borderRadius: '27px',
padding: '12px 24px',
textAlign: 'center',
width: '100%',
'&:disabled': {
opacity: 0.5,
cursor: 'not-allowed',
},
},
},
},
},
});
}
\ No newline at end of file
export type RoleProps = "SUPER_ADMIN" | "ADMIN" | "USER"
export type LoginProps = {
email: string;
password: string;
}
export interface User {
id: number | string,
name: string,
email: string,
first_name: string,
last_name: string,
profile_image: string,
wallet_address: string,
address: string,
city: string
role: RoleProps,
}
export interface LoginResponse {
success: boolean;
data: {
access_token: string,
// expires_in: 3600,
user: User,
}
message: string
}
export interface RegisterProps extends LoginProps {
username: string;
password_confirmation: string;
}
\ No newline at end of file
import { ThemeMode } from "@/config";
export type FontFamily = string;
export type I18n = 'en' | 'ar';
export type DefaultConfigProps = {
fontFamily: FontFamily;
i18n: I18n;
miniDrawer: boolean;
mode: ThemeMode;
}
export type CustomizationProps = {
fontFamily: FontFamily;
i18n: I18n;
miniDrawer: boolean;
mode: ThemeMode;
onChangeMode: (mode: ThemeMode) => void,
onChangeLocalization: (lang: I18n) => void,
onChangeMiniDrawer: (miniDrawer: boolean) => void,
}
export type ImageProps = {
src: string;
alt: string;
width?: string;
height?: string;
}
\ No newline at end of file
import { SimplePaletteColorOptions } from "@mui/material/styles"
export type PaletteThemeProps = {
primary: SimplePaletteColorOptions;
secondary: SimplePaletteColorOptions;
title: SimplePaletteColorOptions;
lightGray: SimplePaletteColorOptions;
}
export type CustomShadowProps = {
button: string;
text: string;
// z1: string;
// z2: string;
primary: string;
primaryButton: string;
secondary: string;
secondaryButton: string;
error: string;
errorButton: string;
warning: string;
warningButton: string;
info: string;
infoButton: string;
success: string;
successButton: string;
grey: string;
greyButton: string;
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment