feat: notifications, redux, tailwind

This commit is contained in:
2025-07-27 11:41:43 +04:00
parent 5e6cfe8070
commit 948480c219
38 changed files with 9594 additions and 2229 deletions

View File

@ -1,3 +0,0 @@
*.js
*.mjs
*.cjs

View File

@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

9
.gitignore vendored
View File

@ -102,7 +102,6 @@ dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
@ -123,11 +122,9 @@ dist
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.yarn
.DS_Store
.idea
.yarnrc.yml

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v24.3.0

View File

@ -1,6 +1 @@
# Ignore artifacts:
build
coverage
# Ignore all HTML files:
**/*.html
.next

View File

@ -1,6 +1,39 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": false
"singleAttributePerLine": true,
"singleQuote": false,
"semi": true,
"quoteProps": "consistent",
"bracketSpacing": true,
"trailingComma": "es5",
"tabWidth": 4,
"bracketSameLine": true,
"arrowParens": "avoid",
"plugins": [
"@ianvs/prettier-plugin-sort-imports"
],
"importOrder": [
".*styles.css$",
"dayjs",
"^react$",
"^next$",
"^next/.*$",
"<BUILTIN_MODULES>",
"<THIRD_PARTY_MODULES>",
"^@mantine/(.*)$",
"^@mantinex/(.*)$",
"^@mantine-tests/(.*)$",
"^@docs/(.*)$",
"^@/.*$",
"^../(?!.*.css$).*$",
"^./(?!.*.css$).*$",
"\\.css$"
],
"overrides": [
{
"files": "*.mdx",
"options": {
"printWidth": 70
}
}
]
}

35
.prettierrc.mjs Normal file
View File

@ -0,0 +1,35 @@
/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */
const config = {
printWidth: 100,
singleQuote: true,
trailingComma: 'es5',
plugins: ['@ianvs/prettier-plugin-sort-imports'],
importOrder: [
'.*styles.css$',
'',
'dayjs',
'^react$',
'^next$',
'^next/.*$',
'<BUILTIN_MODULES>',
'<THIRD_PARTY_MODULES>',
'^@mantine/(.*)$',
'^@mantinex/(.*)$',
'^@mantine-tests/(.*)$',
'^@docs/(.*)$',
'^@/.*$',
'^../(?!.*.css$).*$',
'^./(?!.*.css$).*$',
'\\.css$',
],
overrides: [
{
files: '*.mdx',
options: {
printWidth: 70,
},
},
],
};
export default config;

2
.stylelintignore Normal file
View File

@ -0,0 +1,2 @@
.next
out

28
.stylelintrc.json Normal file
View File

@ -0,0 +1,28 @@
{
"extends": ["stylelint-config-standard-scss"],
"rules": {
"custom-property-pattern": null,
"selector-class-pattern": null,
"scss/no-duplicate-mixins": null,
"declaration-empty-line-before": null,
"declaration-block-no-redundant-longhand-properties": null,
"alpha-value-notation": null,
"custom-property-empty-line-before": null,
"property-no-vendor-prefix": null,
"color-function-notation": null,
"length-zero-no-unit": null,
"selector-not-notation": null,
"no-descending-specificity": null,
"comment-empty-line-before": null,
"scss/at-mixin-pattern": null,
"scss/at-rule-no-unknown": null,
"value-keyword-case": null,
"media-feature-range-notation": null,
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["global"]
}
]
}
}

View File

@ -1,5 +1,37 @@
# Mantine Next Template
# Mantine Next.js template
Get started with the template by clicking `Use this template` button on the top of the page.
This is a template for [Next.js](https://nextjs.org/) app router + [Mantine](https://mantine.dev/).
If you want to use pages router instead, see [next-pages-template](https://github.com/mantinedev/next-pages-template).
[Documentation](https://mantine.dev/guides/next/)
## Features
This template comes with the following features:
- [PostCSS](https://postcss.org/) with [mantine-postcss-preset](https://mantine.dev/styles/postcss-preset)
- [TypeScript](https://www.typescriptlang.org/)
- [Storybook](https://storybook.js.org/)
- [Jest](https://jestjs.io/) setup with [React Testing Library](https://testing-library.com/docs/react-testing-library/intro)
- ESLint setup with [eslint-config-mantine](https://github.com/mantinedev/eslint-config-mantine)
## npm scripts
### Build and dev scripts
- `dev` start dev server
- `build` bundle application for production
- `analyze` analyzes application bundle with [@next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer)
### Testing scripts
- `typecheck` checks TypeScript types
- `lint` runs ESLint
- `prettier:check` checks files with Prettier
- `jest` runs jest tests
- `jest:watch` starts jest watch
- `test` runs `jest`, `prettier:check`, `lint` and `typecheck` scripts
### Other scripts
- `storybook` starts storybook dev server
- `storybook:build` build production storybook bundle to `storybook-static`
- `prettier:write` formats all files with Prettier

21
eslint.config.mjs Normal file
View File

@ -0,0 +1,21 @@
import mantine from "eslint-config-mantine";
import tseslint from "typescript-eslint";
export default tseslint.config(
...mantine,
{ ignores: ["**/*.{mjs,cjs,js,d.ts,d.mts}"] },
{
files: ["**/*.story.tsx"],
rules: {
"no-console": "off",
},
},
{
files: ["**/*.{ts,tsx}"],
rules: {
"no-console": "off",
"react/jsx-curly-brace-presence": "off",
"curly": "off",
},
}
);

16
jest.config.cjs Normal file
View File

@ -0,0 +1,16 @@
const nextJest = require('next/jest');
const createJestConfig = nextJest({
dir: './',
});
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.cjs'],
moduleNameMapper: {
'^@/components/(.*)$': '<rootDir>/components/$1',
'^@/pages/(.*)$': '<rootDir>/pages/$1',
},
testEnvironment: 'jest-environment-jsdom',
};
module.exports = createJestConfig(customJestConfig);

27
jest.setup.cjs Normal file
View File

@ -0,0 +1,27 @@
require('@testing-library/jest-dom');
const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);
window.HTMLElement.prototype.scrollIntoView = () => {};
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
class ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
}
window.ResizeObserver = ResizeObserver;

View File

@ -1,12 +1,16 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
experimental: {
optimizePackageImports: [
"@mantine/core",
"@mantine/hooks",
],
},
}
import bundleAnalyzer from '@next/bundle-analyzer';
export default nextConfig
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
});
export default withBundleAnalyzer({
output: "standalone",
reactStrictMode: false,
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
optimizePackageImports: ['@mantine/core', '@mantine/hooks'],
},
});

View File

@ -9,38 +9,63 @@
"lint": "next lint"
},
"dependencies": {
"@mantine/core": "^8.2.1",
"@mantine/dates": "^8.2.1",
"@mantine/dropzone": "^8.2.1",
"@mantine/form": "^8.2.1",
"@mantine/hooks": "^8.2.1",
"@mantine/modals": "^8.2.1",
"@mantine/core": "8.1.2",
"@mantine/form": "^8.1.3",
"@mantine/hooks": "8.1.2",
"@mantine/notifications": "^8.2.1",
"@next/bundle-analyzer": "^15.3.3",
"@reduxjs/toolkit": "^2.8.2",
"@tabler/icons-react": "^3.34.1",
"@tabler/icons-react": "^3.34.0",
"@tailwindcss/postcss": "^4.1.11",
"@tanstack/react-query": "^5.83.0",
"dayjs": "^1.11.13",
"lodash": "^4.17.21",
"axios": "^1.11.0",
"classnames": "^2.5.1",
"framer-motion": "^12.23.7",
"i18n-iso-countries": "^7.14.0",
"libphonenumber-js": "^1.12.10",
"next": "15.3.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-imask": "^7.6.1",
"react-redux": "^9.2.0",
"tailwind-preset-mantine": "^2.1.0",
"tailwindcss": "^4.1.11"
"redux-persist": "^6.0.0",
"sharp": "^0.34.3"
},
"devDependencies": {
"@types/lodash": "^4",
"@types/node": "22.13.11",
"@types/react": "19.0.12",
"@types/react-dom": "19.0.4",
"eslint": "9.23.0",
"eslint-config-next": "15.2.3",
"postcss": "^8.5.3",
"@babel/core": "^7.27.4",
"@eslint/js": "^9.29.0",
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
"@storybook/nextjs": "^8.6.8",
"@storybook/react": "^8.6.8",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/eslint-plugin-jsx-a11y": "^6",
"@types/jest": "^29.5.14",
"@types/node": "^22.13.11",
"@types/react": "19.1.8",
"@types/react-redux": "^7.1.34",
"@types/redux-persist": "^4.3.1",
"autoprefixer": "^10.4.21",
"babel-loader": "^10.0.0",
"eslint": "^9.29.0",
"eslint-config-mantine": "^4.0.3",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",
"jest": "^30.0.0",
"jest-environment-jsdom": "^30.0.0",
"postcss": "^8.5.6",
"postcss-preset-mantine": "1.17.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "3.6.2",
"typescript": "5.8.2"
"prettier": "^3.5.3",
"storybook": "^8.6.8",
"storybook-dark-mode": "^4.0.2",
"stylelint": "^16.20.0",
"stylelint-config-standard-scss": "^15.0.1",
"tailwindcss": "^4.1.11",
"ts-jest": "^29.4.0",
"typescript": "5.8.3",
"typescript-eslint": "^8.34.0"
},
"packageManager": "yarn@4.9.2"
}

View File

@ -1,7 +1,7 @@
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
"postcss-preset-mantine": {},
"@tailwindcss/postcss": {},
"postcss-simple-vars": {
variables: {
"mantine-breakpoint-xs": "36em",
@ -12,4 +12,4 @@ module.exports = {
},
},
},
}
};

View File

@ -1 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><g fill="none" fill-rule="evenodd"><rect width="500" height="500" fill="#339AF0" rx="250"/><g fill="#FFF"><path fill-rule="nonzero" d="M202.055 135.706c-6.26 8.373-4.494 20.208 3.944 26.42 29.122 21.45 45.824 54.253 45.824 90.005 0 35.752-16.702 68.559-45.824 90.005-8.436 6.215-10.206 18.043-3.944 26.42 6.26 8.378 18.173 10.13 26.611 3.916a153.835 153.835 0 0024.509-22.54h53.93c10.506 0 19.023-8.455 19.023-18.885 0-10.43-8.517-18.886-19.023-18.886h-29.79c8.196-18.594 12.553-38.923 12.553-60.03s-4.357-41.436-12.552-60.03h29.79c10.505 0 19.022-8.455 19.022-18.885 0-10.43-8.517-18.886-19.023-18.886h-53.93a153.835 153.835 0 00-24.509-22.54c-8.438-6.215-20.351-4.46-26.61 3.916z"/><path d="M171.992 246.492c0-15.572 12.624-28.195 28.196-28.195 15.572 0 28.195 12.623 28.195 28.195 0 15.572-12.623 28.196-28.195 28.196-15.572 0-28.196-12.624-28.196-28.196z"/></g></g></svg>
<svg width="41" height="47" viewBox="0 0 41 47" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_431_24446)">
<path opacity="0.958" fill-rule="evenodd" clip-rule="evenodd" d="M20.2179 -0.0939941C20.406 -0.0939941 20.5941 -0.0939941 20.7822 -0.0939941C27.0194 3.59767 33.2885 7.26367 39.5895 10.904C33.2187 14.6831 26.8242 18.4118 20.406 22.09C13.9877 18.4118 7.59324 14.6831 1.22253 10.904C7.56616 7.23297 13.898 3.56697 20.2179 -0.0939941ZM19.6537 3.85401C19.9938 3.85239 20.3073 3.94639 20.5941 4.13601C24.2301 6.39201 27.8663 8.64801 31.5024 10.904C23.6659 11.0293 15.8296 11.0293 7.99318 10.904C11.9233 8.59642 15.8101 6.24642 19.6537 3.85401Z" fill="#44A8C6"/>
<path opacity="0.962" fill-rule="evenodd" clip-rule="evenodd" d="M-0.0939941 13.442C6.3424 16.991 12.7369 20.6257 19.0895 24.346C19.2776 31.8649 19.3402 39.3849 19.2776 46.906C19.0895 46.906 18.9014 46.906 18.7133 46.906C12.4971 43.203 6.22796 39.5684 -0.0939941 36.002C-0.0939941 28.482 -0.0939941 20.962 -0.0939941 13.442ZM2.91518 19.646C6.9531 26.4762 10.9653 33.3382 14.9519 40.232C10.9005 38.3163 6.91964 36.2169 3.00922 33.934C2.91518 29.1718 2.88385 24.4092 2.91518 19.646Z" fill="#334B63"/>
<path opacity="0.972" fill-rule="evenodd" clip-rule="evenodd" d="M40.906 13.442C40.906 21.0246 40.906 28.6074 40.906 36.19C34.5741 39.6675 28.305 43.2395 22.0986 46.906C21.9732 46.906 21.8479 46.906 21.7225 46.906C21.6911 39.3858 21.7225 31.8658 21.8165 24.346C28.1747 20.6832 34.5378 17.0485 40.906 13.442ZM25.8601 40.326C29.6787 33.4443 33.5969 26.6137 37.6147 19.834C37.7401 24.534 37.7401 29.234 37.6147 33.934C33.7364 36.1387 29.8183 38.2693 25.8601 40.326Z" fill="#3C83B4"/>
</g>
<defs>
<clipPath id="clip0_431_24446">
<rect width="41" height="47" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 1.8 KiB

16
src/.storybook/main.ts Normal file
View File

@ -0,0 +1,16 @@
import type { StorybookConfig } from '@storybook/nextjs';
const config: StorybookConfig = {
core: {
disableWhatsNewNotifications: true,
disableTelemetry: true,
enableCrashReports: false,
},
stories: ['../components/**/*.(stories|story).@(js|jsx|ts|tsx)'],
addons: ['storybook-dark-mode'],
framework: {
name: '@storybook/nextjs',
options: {},
},
};
export default config;

View File

@ -0,0 +1,36 @@
import '@mantine/core/styles.css';
import React, { useEffect } from 'react';
import { addons } from '@storybook/preview-api';
import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
import { MantineProvider, useMantineColorScheme } from '@mantine/core';
import { theme } from '../theme';
export const parameters = {
layout: 'fullscreen',
options: {
showPanel: false,
storySort: (a, b) => {
return a.title.localeCompare(b.title, undefined, { numeric: true });
},
},
};
const channel = addons.getChannel();
function ColorSchemeWrapper({ children }: { children: React.ReactNode }) {
const { setColorScheme } = useMantineColorScheme();
const handleColorScheme = (value: boolean) => setColorScheme(value ? 'dark' : 'light');
useEffect(() => {
channel.on(DARK_MODE_EVENT_NAME, handleColorScheme);
return () => channel.off(DARK_MODE_EVENT_NAME, handleColorScheme);
}, [channel]);
return <>{children}</>;
}
export const decorators = [
(renderStory: any) => <ColorSchemeWrapper>{renderStory()}</ColorSchemeWrapper>,
(renderStory: any) => <MantineProvider theme={theme}>{renderStory()}</MantineProvider>,
];

7
src/app/global.css Normal file
View File

@ -0,0 +1,7 @@
@import "tailwindcss";
body {
@mixin light {
background-color: whitesmoke;
}
}

View File

@ -1,2 +0,0 @@
@import "tailwind-preset-mantine";
@import "./theme.css";

View File

@ -1,41 +1,54 @@
import "@mantine/core/styles.css"
import "@mantine/dates/styles.css"
import "@mantine/notifications/styles.css"
import '@mantine/dropzone/styles.css';
import React from "react"
import "@mantine/core/styles.css";
import "@mantine/notifications/styles.css";
import React, { ReactNode } from "react";
import {
ColorSchemeScript,
mantineHtmlProps,
MantineProvider,
} from "@mantine/core"
import { theme } from "./theme"
import "./globals.css"
import { Notifications } from "@mantine/notifications"
import { ModalsProvider } from "@mantine/modals"
} from "@mantine/core";
import { theme } from "@/theme";
import "@/app/global.css";
import { Notifications } from "@mantine/notifications";
import ReduxProvider from "@/providers/ReduxProvider";
export const metadata = {
title: "Mantine Next.js template",
description: "I am using Mantine with Next.js!",
}
title: "CRM LogiDex",
description: "CRM LogiDex",
};
export default function RootLayout({ children }: { children: any }) {
type Props = {
children: ReactNode;
};
export default function RootLayout({ children }: Props) {
return (
<html lang="en" {...mantineHtmlProps}>
<html
lang="ru"
{...mantineHtmlProps}>
<head>
<ColorSchemeScript />
<link rel="shortcut icon" href="/favicon.svg" />
<ColorSchemeScript defaultColorScheme={"auto"} />
<link
rel="shortcut icon"
href="/favicon.svg"
/>
<link
rel="stylesheet"
href="global.css"
/>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, user-scalable=no"
/>
<title />
</head>
<body>
<MantineProvider theme={theme}>
<Notifications />
<ModalsProvider>{children}</ModalsProvider>
<MantineProvider
theme={theme}
defaultColorScheme={"auto"}>
<ReduxProvider>{children}</ReduxProvider>
<Notifications position="bottom-right" />
</MantineProvider>
</body>
</html>
)
);
}

View File

@ -1,52 +1,11 @@
import { ColorSchemesSwitcher } from "@/components/color-schemes-switcher"
import {
AppShell,
AppShellHeader,
AppShellMain,
Text,
Title,
} from "@mantine/core"
import { ColorSchemeToggle } from "@/components/ColorSchemeToggle/ColorSchemeToggle";
import { Welcome } from "@/components/Welcome/Welcome";
export default function Home() {
export default function HomePage() {
return (
<AppShell header={{ height: 60 }} padding="md">
<AppShellHeader></AppShellHeader>
<AppShellMain>
<Title className="text-center mt-20">
Welcome to{" "}
<Text
inherit
variant="gradient"
component="span"
gradient={{ from: "pink", to: "yellow" }}
>
Mantine
</Text>{" "}
+
<Text
inherit
variant="gradient"
component="span"
gradient={{ from: "blue", to: "green" }}
>
TailwindCSS
</Text>
</Title>
<Text
className="text-bold text-center text-gray-700 dark:text-gray-300 max-w-[500px] mx-auto mt-xl"
ta="center"
size="lg"
maw={580}
mx="auto"
mt="xl"
>
This starter Next.js project includes a minimal setup for
Mantine with TailwindCSS. To get started edit page.tsx file.
</Text>
<div className="flex justify-center mt-10">
<ColorSchemesSwitcher />
</div>
</AppShellMain>
</AppShell>
)
<>
<Welcome />
<ColorSchemeToggle />
</>
);
}

View File

@ -1,133 +0,0 @@
/** This file is autogenerated by the script. Do not edit it manually. */
@theme {
/* colors - all */
/* colors - variant specific */
/* breakpoints */
--breakpoint-*: initial;
--breakpoint-xs: 36em;
--breakpoint-sm: 48em;
--breakpoint-md: 62em;
--breakpoint-lg: 75em;
--breakpoint-xl: 88em;
/* readd back tailwind's default containers vars to fix #24 */
--size-3xs: 16rem;
--size-2xs: 18rem;
--size-xs: 20rem;
--size-sm: 24rem;
--size-md: 28rem;
--size-lg: 32rem;
--size-xl: 36rem;
--size-2xl: 42rem;
--size-3xl: 48rem;
--size-4xl: 56rem;
--size-5xl: 64rem;
--size-6xl: 72rem;
--size-7xl: 80rem;
--container-3xs: var(--size-3xs);
--container-2xs: var(--size-2xs);
--container-xs: var(--size-xs);
--container-sm: var(--size-sm);
--container-md: var(--size-md);
--container-lg: var(--size-lg);
--container-xl: var(--size-xl);
--container-2xl: var(--size-2xl);
--container-3xl: var(--size-3xl);
--container-4xl: var(--size-4xl);
--container-5xl: var(--size-5xl);
--container-6xl: var(--size-6xl);
--container-7xl: var(--size-7xl);
--width-3xs: var(--size-3xs);
--width-2xs: var(--size-2xs);
--width-xs: var(--size-xs);
--width-sm: var(--size-sm);
--width-md: var(--size-md);
--width-lg: var(--size-lg);
--width-xl: var(--size-xl);
--width-2xl: var(--size-2xl);
--width-3xl: var(--size-3xl);
--width-4xl: var(--size-4xl);
--width-5xl: var(--size-5xl);
--width-6xl: var(--size-6xl);
--width-7xl: var(--size-7xl);
--min-width-3xs: var(--size-3xs);
--min-width-2xs: var(--size-2xs);
--min-width-xs: var(--size-xs);
--min-width-sm: var(--size-sm);
--min-width-md: var(--size-md);
--min-width-lg: var(--size-lg);
--min-width-xl: var(--size-xl);
--min-width-2xl: var(--size-2xl);
--min-width-3xl: var(--size-3xl);
--min-width-4xl: var(--size-4xl);
--min-width-5xl: var(--size-5xl);
--min-width-6xl: var(--size-6xl);
--min-width-7xl: var(--size-7xl);
--max-width-3xs: var(--size-3xs);
--max-width-2xs: var(--size-2xs);
--max-width-xs: var(--size-xs);
--max-width-sm: var(--size-sm);
--max-width-md: var(--size-md);
--max-width-lg: var(--size-lg);
--max-width-xl: var(--size-xl);
--max-width-2xl: var(--size-2xl);
--max-width-3xl: var(--size-3xl);
--max-width-4xl: var(--size-4xl);
--max-width-5xl: var(--size-5xl);
--max-width-6xl: var(--size-6xl);
--max-width-7xl: var(--size-7xl);
--height-3xs: var(--size-3xs);
--height-2xs: var(--size-2xs);
--height-xs: var(--size-xs);
--height-sm: var(--size-sm);
--height-md: var(--size-md);
--height-lg: var(--size-lg);
--height-xl: var(--size-xl);
--height-2xl: var(--size-2xl);
--height-3xl: var(--size-3xl);
--height-4xl: var(--size-4xl);
--height-5xl: var(--size-5xl);
--height-6xl: var(--size-6xl);
--height-7xl: var(--size-7xl);
--min-height-3xs: var(--size-3xs);
--min-height-2xs: var(--size-2xs);
--min-height-xs: var(--size-xs);
--min-height-sm: var(--size-sm);
--min-height-md: var(--size-md);
--min-height-lg: var(--size-lg);
--min-height-xl: var(--size-xl);
--min-height-2xl: var(--size-2xl);
--min-height-3xl: var(--size-3xl);
--min-height-4xl: var(--size-4xl);
--min-height-5xl: var(--size-5xl);
--min-height-6xl: var(--size-6xl);
--min-height-7xl: var(--size-7xl);
--max-height-3xs: var(--size-3xs);
--max-height-2xs: var(--size-2xs);
--max-height-xs: var(--size-xs);
--max-height-sm: var(--size-sm);
--max-height-md: var(--size-md);
--max-height-lg: var(--size-lg);
--max-height-xl: var(--size-xl);
--max-height-2xl: var(--size-2xl);
--max-height-3xl: var(--size-3xl);
--max-height-4xl: var(--size-4xl);
--max-height-5xl: var(--size-5xl);
--max-height-6xl: var(--size-6xl);
--max-height-7xl: var(--size-7xl);
}

View File

@ -1,15 +0,0 @@
"use client"
import { createTheme } from "@mantine/core"
export const theme = createTheme({
breakpoints: {
xs: "36em",
sm: "48em",
md: "62em",
lg: "75em",
xl: "88em",
},
})
export default theme

View File

@ -0,0 +1,17 @@
"use client";
import { Button, Group, useMantineColorScheme } from "@mantine/core";
export function ColorSchemeToggle() {
const { setColorScheme } = useMantineColorScheme();
return (
<Group
justify="center"
mt="xl">
<Button onClick={() => setColorScheme("light")}>Light</Button>
<Button onClick={() => setColorScheme("dark")}>Dark</Button>
<Button onClick={() => setColorScheme("auto")}>Auto</Button>
</Group>
);
}

View File

@ -0,0 +1,31 @@
import { Anchor, Text, Title } from "@mantine/core";
export function Welcome() {
return (
<>
<Title
ta="center"
mt={100}
className={"font-bold underline"}>
Welcome to Mantine
</Title>
<Text
c="dimmed"
ta="center"
size="lg"
maw={580}
mx="auto"
mt="xl">
This starter Next.js project includes a minimal setup for server
side rendering, if you want to learn more on Mantine + Next.js
integration follow{" "}
<Anchor
href="https://mantine.dev/guides/next/"
size="lg">
this guide
</Anchor>
. To get started edit page.tsx file.
</Text>
</>
);
}

View File

@ -1,18 +0,0 @@
"use client"
import { Button, Group, useMantineColorScheme } from "@mantine/core"
export function ColorSchemesSwitcher() {
const { setColorScheme, clearColorScheme } = useMantineColorScheme()
return (
<Group>
<Button variant={"filled"} onClick={() => setColorScheme("light")}>
Light
</Button>
<Button onClick={() => setColorScheme("dark")}>Dark</Button>
<Button onClick={() => setColorScheme("auto")}>Auto</Button>
<Button onClick={clearColorScheme}>Clear</Button>
</Group>
)
}

View File

@ -0,0 +1,23 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface AuthState {
phoneNumber: string | null;
}
const initialState: AuthState = {
phoneNumber: null,
};
export const authSlice = createSlice({
name: "authentication",
initialState,
reducers: {
setPhoneNumber: (state, action: PayloadAction<string | null>) => {
state.phoneNumber = action.payload;
},
},
});
export const { setPhoneNumber } = authSlice.actions;
export default authSlice.reducer;

View File

@ -0,0 +1,8 @@
import { combineReducers } from "@reduxjs/toolkit";
import authReducer from "@/lib/features/auth/authSlice";
const rootReducer = combineReducers({
auth: authReducer,
});
export default rootReducer;

View File

@ -0,0 +1 @@
export * from "./notifications";

View File

@ -0,0 +1,46 @@
import { notifications } from "@mantine/notifications";
type CustomNotifications = {
notify: (...params: Parameters<typeof notifications.show>) => void;
success: (...params: Parameters<typeof notifications.show>) => void;
warn: (...params: Parameters<typeof notifications.show>) => void;
error: (...params: Parameters<typeof notifications.show>) => void;
guess: (
ok: boolean,
...params: Parameters<typeof notifications.show>
) => void;
} & typeof notifications;
const customNotifications: CustomNotifications = {
...notifications,
notify: params => {
return notifications.show({
...params,
color: "blue",
});
},
success: params => {
return notifications.show({
...params,
color: "green",
});
},
warn: params => {
return notifications.show({
...params,
color: "yellow",
});
},
error: params => {
return notifications.show({
...params,
color: "red",
});
},
guess: (ok: boolean, params) => {
if (ok) return customNotifications.success(params);
return customNotifications.error(params);
},
};
export { customNotifications as notifications };

28
src/lib/store.ts Normal file
View File

@ -0,0 +1,28 @@
import { configureStore } from "@reduxjs/toolkit";
import { useDispatch } from "react-redux";
import { persistReducer, persistStore } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "@/lib/features/rootReducer";
const persistConfig = {
key: "root",
storage,
whitelist: ["targetService", "verification", "auth"],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false,
}),
});
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();

View File

@ -0,0 +1,23 @@
"use client";
import { ReactNode } from "react";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { persistor, store } from "@/lib/store";
type Props = {
children: ReactNode;
};
export default function ReduxProvider({ children }: Props) {
return (
<Provider store={store}>
{" "}
<PersistGate
loading={null}
persistor={persistor}>
{children}
</PersistGate>
</Provider>
);
}

5
src/redux-persist.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module "redux-persist/lib/storage" {
import { WebStorage } from "redux-persist/es/types";
const localStorage: WebStorage;
export default localStorage;
}

38
src/theme.ts Normal file
View File

@ -0,0 +1,38 @@
import { createTheme, MantineColorsTuple } from "@mantine/core";
export const myColor: MantineColorsTuple = [
"#e2faff",
"#d4eff8",
"#afdce9",
"#87c8db",
"#65b7cf",
"#4aaac7",
"#3fa7c6",
"#2c92af",
"#1b829e",
"#00718c",
];
const radius = "lg";
const size = "lg";
export const theme = createTheme({
colors: {
myColor,
},
primaryColor: "myColor",
components: {
Button: {
defaultProps: {
radius,
size,
},
},
InputBase: {
defaultProps: {
radius,
size,
},
},
},
});

View File

@ -1,28 +1,25 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
"compilerOptions": {
"types": ["node", "jest", "@testing-library/jest-dom"],
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"plugins": [{ "name": "next" }]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

10872
yarn.lock

File diff suppressed because it is too large Load Diff