diff --git a/.changeset/big-lamps-attack.md b/.changeset/big-lamps-attack.md new file mode 100644 index 0000000..55b4367 --- /dev/null +++ b/.changeset/big-lamps-attack.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +added logic for active link styles diff --git a/.changeset/curvy-walls-unite.md b/.changeset/curvy-walls-unite.md new file mode 100644 index 0000000..bb6f932 --- /dev/null +++ b/.changeset/curvy-walls-unite.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +added mantine sliders, dates, buttons stories diff --git a/.changeset/cyan-parents-cough.md b/.changeset/cyan-parents-cough.md new file mode 100644 index 0000000..9eff06c --- /dev/null +++ b/.changeset/cyan-parents-cough.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +added and setup prettier, husky and lint-staged diff --git a/.changeset/dull-glasses-fold.md b/.changeset/dull-glasses-fold.md new file mode 100644 index 0000000..0a55540 --- /dev/null +++ b/.changeset/dull-glasses-fold.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +added usefetchdata hook for all mock data replacing the previous data imports diff --git a/.changeset/four-cougars-battle.md b/.changeset/four-cougars-battle.md new file mode 100644 index 0000000..7aa6102 --- /dev/null +++ b/.changeset/four-cougars-battle.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +updated side nav background color diff --git a/.changeset/large-deers-remember.md b/.changeset/large-deers-remember.md new file mode 100644 index 0000000..e9433cf --- /dev/null +++ b/.changeset/large-deers-remember.md @@ -0,0 +1,40 @@ +--- +"analytics-dashboard": major +--- + +Upgrade to Next.js 14 with App Router and Mantine 7 + +# Summary + +This major release marks a significant upgrade to the website, transitioning from Next.js 13 to 14 with App Router and from Mantine 6 to 7. This upgrade brings about substantial enhancements in performance, accessibility, user experience, and overall development practices. + +# Next.js + +- Project Structure: Aligned the project structure with the recommended guidelines for Next.js and App Router, enhancing organization and maintainability. +- Pages and Layouts: Optimized the pages and layouts structure to align with Next.js and App Router best practices, ensuring a more structured and efficient codebase. +- Pages Metadata: Updated each page's metadata content to provide more accurate and informative descriptions for search engines. +- Error Pages: Deprecated the individual 403, 404, and 500 error pages and adopted a unified "not-found" page for 404 errors and a generic error page for other server-side errors. +- Navigation Progress Bar: Discontinued the navigation progress bar for in-page transitions and implemented a centralized loading.tsx file to handle all in-page loading animations. + +# Mantine + +- CreateStyles: Adopted CSS Modules as the preferred method for styling components, replacing the deprecated createStyles function. +- Sx Prop: Replaced the deprecated sx prop with className or style prop for styling components in Mantine 7.0. +- Theme and ColorScheme: Refactored the theme and colour scheme usage across the codebase to align with Mantine 7.0 conventions. +- Dynamic Multicolor Theme: Deprecated the dynamic multicolour theme change feature and centralized theme configuration in the theme/index.tsx file. + +# New Features + +- Collapsible Side Navigation: Introduced a collapsible side navigation panel for enhanced user interface and navigation experience. +- Active Link Styles: Implemented logic to dynamically apply active link styles to the side navigation menu, providing clear visual cues for the currently selected page. +- Dark/Light Theme Switch: Integrated a dark/light theme toggle switch on the navigation header, enabling users to personalize their viewing experience. +- Loading Animations: Added loading animations to components rendering mock data, providing visual feedback during data fetching and processing. +- Custom useFetch Hook: Developed a custom useFetch hook to streamline data fetching across the application. This hook encapsulates data fetching logic, returning data, loading, and error states, and utilizes the native JavaScript fetch API. +- Code Formatting: Implemented Prettier, Husky, and lint-staged to enforce consistent code formatting and maintain a high level of code quality. +- Clerk Integration: Integrated Clerk (https://clerk.com/) to extend user authentication and management capabilities, providing a seamless user experience. +- NextAuth with Auth0: Implemented NextAuth with Auth0 (https://auth0.com/) for user authentication, leveraging NextAuth's flexibility and Auth0's robust identity management platform. +- Component Documentation Stories: Created component documentation stories using Storybook to provide clear and interactive documentation for each component. + +# Bug Fixes + +- General Bug Fixes: Addressed a variety of bugs and issues to enhance overall stability and performance. diff --git a/.changeset/moody-peaches-train.md b/.changeset/moody-peaches-train.md new file mode 100644 index 0000000..6b4a200 --- /dev/null +++ b/.changeset/moody-peaches-train.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +updated pages metadata bug removed head tags in pages added color mode switch moved color mode switch to header removed theme drawer because no logic for color switching yet diff --git a/.changeset/odd-mails-invent.md b/.changeset/odd-mails-invent.md new file mode 100644 index 0000000..4feff6f --- /dev/null +++ b/.changeset/odd-mails-invent.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +added chats loading animation, updated chats data to fetch using fetch data hook diff --git a/.changeset/plenty-seas-push.md b/.changeset/plenty-seas-push.md new file mode 100644 index 0000000..bc99e04 --- /dev/null +++ b/.changeset/plenty-seas-push.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +updated all pages metatadata diff --git a/.changeset/polite-adults-laugh.md b/.changeset/polite-adults-laugh.md new file mode 100644 index 0000000..c23159b --- /dev/null +++ b/.changeset/polite-adults-laugh.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +removed 403 page because of build time errors diff --git a/.changeset/polite-apples-guess.md b/.changeset/polite-apples-guess.md new file mode 100644 index 0000000..16ff916 --- /dev/null +++ b/.changeset/polite-apples-guess.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': major +--- + +upgraded to next 14 and mantine 7 diff --git a/.changeset/seven-timers-flash.md b/.changeset/seven-timers-flash.md new file mode 100644 index 0000000..9c50ded --- /dev/null +++ b/.changeset/seven-timers-flash.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +refactored layouts to match recommended layouts file structure by next docs 13 diff --git a/.changeset/smooth-llamas-thank.md b/.changeset/smooth-llamas-thank.md new file mode 100644 index 0000000..50042b1 --- /dev/null +++ b/.changeset/smooth-llamas-thank.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +Added storybook component stories diff --git a/.changeset/soft-wolves-push.md b/.changeset/soft-wolves-push.md new file mode 100644 index 0000000..70ef7f7 --- /dev/null +++ b/.changeset/soft-wolves-push.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +refactored 404 and 500 pages because they are ootb & designated special pages by nextjs diff --git a/.changeset/tender-timers-shop.md b/.changeset/tender-timers-shop.md new file mode 100644 index 0000000..9165dcf --- /dev/null +++ b/.changeset/tender-timers-shop.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': patch +--- + +finished app layout migrations diff --git a/.changeset/yellow-radios-destroy.md b/.changeset/yellow-radios-destroy.md new file mode 100644 index 0000000..2ff81ff --- /dev/null +++ b/.changeset/yellow-radios-destroy.md @@ -0,0 +1,5 @@ +--- +'analytics-dashboard': minor +--- + +setup next-auth & added auth0 provicder diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..53b061a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..b5c68e5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..2f28cea 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea for this project title: '' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 0000000..712cef7 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,25 @@ +# Workflow name +name: 'Chromatic Deployment' + +# Event for the workflow +on: + push: + branches: [master] + pull_request: + branches: [master] + +# List of jobs +jobs: + test: + # Operating System + runs-on: ubuntu-latest + # Job steps + steps: + - uses: actions/checkout@v3 + - run: yarn + #πŸ‘‡ Adds Chromatic as a step in the workflow + - uses: chromaui/action@v1 + # Options required for Chromatic's GitHub Action + with: + #πŸ‘‡ Chromatic projectToken, see https://storybook.js.org/tutorials/intro-to-storybook/react/en/deploy/ to obtain it + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7750672..e55c9d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,4 +26,4 @@ jobs: - name: Create Release Pull Request uses: changesets/action@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index fb30cbd..53c5b09 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,28 @@ yarn-error.log* next-env.d.ts .idea + +### StorybookJs ### +# gitignore template for the Storybook, UI guide for front apps +# website: https://storybook.js.org/ + +storybook-static/ + +### yarn ### +# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored + +.yarn/* +!.yarn/releases +!.yarn/patches +!.yarn/plugins +!.yarn/sdks +!.yarn/versions + +# if you are NOT using Zero-installs, then: +# comment the following lines +!.yarn/cache + +# and uncomment the following lines +# .pnp.* + +# End of https://www.toptal.com/developers/gitignore/api/yarn diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..610c2a5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm test diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1b8ac88 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +# Ignore artifacts: +build +coverage diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..937375d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "semi": true, + "singleQuote": true +} diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 0000000..1b8c3db --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,41 @@ +import type { StorybookConfig } from '@storybook/nextjs'; +import { resolve } from 'node:path'; +import path from 'path'; + +const config: StorybookConfig = { + stories: [ + '../stories/**/*.mdx', + '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)', + '../components/**/*.stories.mdx', + '../components/**/*.stories.@(js|jsx|ts|tsx)', + ], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-onboarding', + '@storybook/addon-interactions', + '@storybook/addon-styling-webpack', + ], + framework: { + name: '@storybook/nextjs', + options: { + nextConfigPath: resolve(__dirname, '../../next.config.js'), + }, + }, + docs: { + autodocs: 'tag', + }, + webpackFinal: async (config, { configType }) => { + if (!config.resolve) { + return config; + } + + config.resolve.alias = { + ...config.resolve.alias, + '@': path.resolve(__dirname, '..'), + }; + + return config; + }, +}; +export default config; diff --git a/.storybook/manager.ts b/.storybook/manager.ts new file mode 100644 index 0000000..b2fb282 --- /dev/null +++ b/.storybook/manager.ts @@ -0,0 +1,8 @@ +// .storybook/manager.js + +import { addons } from '@storybook/manager-api'; +import myTheme from './theme'; + +addons.setConfig({ + theme: myTheme, +}); diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 0000000..af4f3cf --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,55 @@ +// Import styles of packages that you've installed. +// All packages except `@mantine/hooks` require styles imports +import '@mantine/core/styles.css'; +import '@mantine/dates/styles.css'; +import '@mantine/tiptap/styles.css'; +import '@mantine/carousel/styles.css'; +import '@mantine/notifications/styles.css'; +import 'mantine-datatable/styles.layer.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 { themes } from '@storybook/theming'; +import { Preview } from '@storybook/react'; +// @ts-ignore +import { myTheme } from '../theme'; + +// theme.ts file from previous step + +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) => ( + {renderStory()} + ), + (renderStory: any) => ( + {renderStory()} + ), +]; + +const preview: Preview = { + parameters: { + nextjs: { + appDirectory: true, + }, + docs: { + theme: themes.normal, + }, + }, + decorators: decorators, +}; diff --git a/.storybook/theme.ts b/.storybook/theme.ts new file mode 100644 index 0000000..d056e77 --- /dev/null +++ b/.storybook/theme.ts @@ -0,0 +1,13 @@ +// .storybook/YourTheme.js + +import { create } from '@storybook/theming/create'; + +export default create({ + base: 'light', + brandTitle: 'Mantine analytics dashboard', + brandUrl: 'https://mantine-analytics-dashboard.netlify.app/', + brandTarget: '_blank', + + // + colorPrimary: '#2378c3', +}); diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz new file mode 100644 index 0000000..8a54840 Binary files /dev/null and b/.yarn/install-state.gz differ diff --git a/README.md b/README.md index 0dd6ae2..ae0592d 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,121 @@ -![thumbnail-img-b](https://github.com/design-sparx/mantine-analytics-dashboard/assets/26582923/b82ec1c2-479a-4eb3-9ef2-0c5e6435c78c) +

+logo +

+ +

+ + License + + + GitHub issues open + + + GitHub issues closed + + + Live Demo + + Download + + + Netlify Deployment Status + + + Storybook Deployment Status + +
+ + GitHub stars + +

+ +- [Live preview](https://mantine-analytics-dashboard.netlify.app/) +- [Components preview](https://6546507b657a74164abf2db6-oniqlpqtfs.chromatic.com/) +- [Medium](https://medium.com/stackademic/how-i-built-an-open-source-admin-dashboard-template-with-mantine-and-next-js-4f00a21ce04f) + +# About + +A professional admin & dashboard template built using on [Mantine 7](https://mantine.dev/) that comes with hundreds of +UI components, forms, tables, charts, pages and icons. This template is built +using [Next v14](https://nextjs.org/), [React](https://react.dev/), [Apex Charts](https://apexcharts.com/),[Mantine DataTable](https://icflorescu.github.io/mantine-datatable/) +and [Storybook](https://storybook.js.org/). + +![preview image](public/dashboard.png) + +# Features + +- **Customizable:** You don't need to be an expert to customize the template. Our code is very readable and + well-documented. +- **Fully Responsive:** With mobile, tablet & desktop support it doesn't matter what device you're using. Antd Dashboard + is responsive in all browsers. +- **Cross-Browser:** Our themes are working perfectly with Chrome, Firefox, Opera, and Edge. We're working hard to + support them. +- **Clean Code:** We strictly follow Ant Design's guidelines to make your integration as easy as possible. All code is + handwritten. +- **Regular Updates:** From time to time you'll receive an update containing new components, improvements, and bug + fixes. + +# Tech stack + +To make this template awesome, I used the following packages: + +## Core + +- **Next js v14:** Next.js is an open-source web development framework created by the private company Vercel providing + React-based web applications with server-side rendering and static website generation. +- **Mantine v7:** Mantine is a React UI components library. It's built on top of React and TypeScript, and provides a + variety of + components and hooks for building high-performance web applications. +- **React v18:** React is a free and open-source front-end JavaScript library for building user interfaces based on + components. +- **Typescript v5:** TypeScript is a free and open-source high-level programming language developed by Microsoft that + adds static typing with optional type annotations to JavaScript. +- **Storybook v7:** Storybook is a free, open-source tool for developing UI components in isolation. It's used by web + developers to improve their UI development workflow and build better web applications. +- **Changeset CLI v2:** Changeset is a package that helps in managing my versions and changelogs. +- **NextAuth v4:** NextAuth.js is a flexible and secure authentication library that can be used for client-side + authentication in Next.js. +- **Tabler icons v2:** Tabler Icons is a free, open-source icon library with over 4,700 icons. The icons are designed + with a modern aesthetic and are suitable for a wide range of applications. +- **Mantine datatable v7:** Mantine DataTable is a React component that can be used to create data-rich user interfaces. + It is a table component that is aware of dark themes and is designed for Mantine UI. +- **Lodash v4:** A JavaScript utility library delivering consistency, modularity, performance, & extras. +- **Apex chart v3:s** ApexCharts is a free, open-source JavaScript charting library that allows developers to create + interactive data visualizations for web pages. It can be used for both commercial and non-commercial projects. +- **Dayjs v1:** Day.js is a JavaScript library that handles dates and times. +- **Tiptap v2:** A headless, framework-agnostic and extendable rich text editor, based on ProseMirror. +- **Fullcalendar v6**: FullCalendar is a JavaScript event calendar with over 300 settings. It's open source and has a + free core. +- **Dnd-Kit v6:** Dnd-kit is a lightweight, modular, and extensible drag-and-drop toolkit for React. It is also + accessible and performant. +- **Embla carousel v7:** Embla Carousel is a lightweight carousel library with fluid motion and precise swiping. +- **React simple maps v3:** An svg map chart component built with and for React. +- **Clerk/nextjs v4:** Clerk Next.js is a wrapper around Clerk React. It allows users to use the hooks that Clerk React + provides. +- **React countup v6:** A React component wrapper around CountUp.js. + +## Dev + +- **Prettier v3:** Prettier is a code formatter that automatically formats code to ensure it is consistent and easy to + read. +- **Husky v8:** Husky is a tool that makes it easier to work with git hooks. Prettier is a code formatter. +- **Lint staged v15:** Lint-staged will automatically add any modifications to the commit as long as there are no + errors. +- **Postcss v8:** PostCSS is a JavaScript library that uses plugins to transform CSS. It transpiles CSS into an abstract + syntax tree, which is then represented by JavaScript objects. + +# Quick start + +## Download + +- Clone this repo git clone `https://github.com/design-sparx/mantine-analytics-dashboard.git` +- [Download from GitHub](https://github.com/design-sparx/mantine-analytics-dashboard/archive/refs/heads/main.zip) -[![Netlify Status](https://api.netlify.com/api/v1/badges/72758493-91b7-4ec0-ab26-697db44db93e/deploy-status)](https://app.netlify.com/sites/mantine-analytics-dashboard/deploys) - -[Live Preview](https://mantine-analytics-dashboard.netlify.app/) - -## Quick start - -This is a Next.js project bootstrapped with create-next-app. You'll need to install Node.js (v16 or up) before using -Dashboard. - -Before proceeding, please ensure you have the following software installed on your computer. - -- Node -- Yarn (optional but recommended) -- Git command line tools - -## Useful links -- Download Git CLI - - - Windows: https://git-scm.com/download/windows - - Mac: https://git-scm.com/download/mac -- Download Node - https://nodejs.org/en/ -- Download Yarn CLI - https://yarnpkg.com/lang/en/docs/install/ -- Download IDE - - vsCode: https://code.visualstudio.com/ - - Jetbrains webstorm - https://www.jetbrains.com/webstorm/ +## Build tools -## Project Setup -In your root project folder, open your terminal and run the command below. +You'll need to install Node.js. +Once Node.js is installed, run npm install to install the rest of the template's dependencies. All dependencies will be +downloaded to the node_modules directory. ```bash copy npm install @@ -39,38 +128,111 @@ local webserver at http://localhost:3000, run the following command. npm run dev ``` -## Build tools -Start a local webserver at http://localhost:3000 and detect file changes: - -```bash copy -npm run dev -``` - -Compile, optimize, minify and uglify all source files to build/: +Compile, optimize, minify and uglify all source files to build/ ```bash copy npm run build ``` -## File structure -Inside the zip-file you'll find the following directories and files. Both compiled and minified distrubution files, as +# File structure + +Inside the zip-file you'll find the following directories and files. Both compiled and minified distribution files, as +Inside the zip file, you'll find the following directories and files. Both compiled and minified distribution files, as well as the source files are included in the package. ``` -theme/ - β”œβ”€β”€ .gitignore - β”œβ”€β”€ package.json - β”œβ”€β”€ package-lock.json - β”œβ”€β”€ README.md - β”œβ”€β”€ components/ - β”œβ”€β”€ layout/ - β”œβ”€β”€ mocks/ - β”œβ”€β”€ pages/ - β”œβ”€β”€ routes/ - β”œβ”€β”€ styles/ - β”œβ”€β”€ types/ - β”œβ”€β”€ utils/ - β”œβ”€β”€ public/ - β”‚ β”œβ”€β”€ index.html - β”‚ └── manifest.json +mantine-analytics-dashboard/ +β”œβ”€β”€ .changeset +β”œβ”€β”€ .github +β”œβ”€β”€ .gitignore +β”œβ”€β”€ .editorconfig +β”œβ”€β”€ .prettierignore +β”œβ”€β”€ .prettierrc +β”œβ”€β”€ README.md +β”œβ”€β”€ CHANGELOG.md +β”œβ”€β”€ LICENSE +β”œβ”€β”€ index.html +β”œβ”€β”€ package.json +β”œβ”€β”€ tsconfig.json +β”œβ”€β”€ next.config.js +β”œβ”€β”€ postcss.config.cjs +β”œβ”€β”€ clerkMiddleware.ts +β”œβ”€β”€ yarn.lock +β”œβ”€β”€ public/ +β”‚ β”œβ”€β”€ mocks/ +β”‚ β”œβ”€β”€ _redirects +β”‚ β”œβ”€β”€ favicon.ico +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ .changeset/ +β”‚ β”œβ”€β”€ .github/ +β”‚ β”œβ”€β”€ .husty/ +β”‚ β”œβ”€β”€ .storybook/ +β”‚ β”œβ”€β”€ .yarn/ +β”‚ β”œβ”€β”€ app/ +β”œβ”€β”€β”€β”€β”€β”€ api/ +β”œβ”€β”€β”€β”€β”€β”€ error.tsx +β”œβ”€β”€β”€β”€β”€β”€ error.module.css +β”œβ”€β”€β”€β”€β”€β”€ global.css +β”œβ”€β”€β”€β”€β”€β”€ layout.tsx +β”œβ”€β”€β”€β”€β”€β”€ loading.tsx +β”œβ”€β”€β”€β”€β”€β”€ not-found.tsx +β”œβ”€β”€β”€β”€β”€β”€ page.module.css +β”œβ”€β”€β”€β”€β”€β”€ page.tsx +β”‚ β”œβ”€β”€ components/ +β”‚ β”œβ”€β”€ hooks/ +β”‚ β”œβ”€β”€ layout/ +β”‚ β”œβ”€β”€ providers/ +β”‚ β”œβ”€β”€ public/ +β”‚ β”œβ”€β”€ routes/ +β”‚ β”œβ”€β”€ styles/ +β”‚ β”œβ”€β”€ theme/ +β”‚ β”œβ”€β”€ types/ +β”‚ β”œβ”€β”€ utils/ +└── ``` + +# Contributing and Support + +I welcome all developers and enthusiasts to contribute to the growth of our open-source admin dashboard template. +Contributing is a collaborative process that empowers us to collectively enhance the template’s capabilities and +quality. To get started: + +- Fork the Repository: Fork the template’s GitHub repository to your own GitHub account. +- Clone the Fork: Clone the forked repository to your local machine using Git. +- Create a Branch: Create a new branch for your contributions to keep the main codebase intact. +- Implement Changes: Make your desired changes, add new components, or refine existing features. +- Commit and Push: Commit your changes to the new branch and push them to your GitHub fork. +- Submit a Pull Request: Submit a pull request from your forked repository to the main template repository. Your changes + will be reviewed and potentially merged. + +# Reporting Issues and Seeking Help + +Should you encounter any issues or need assistance while using the template, we’re here to help: + +- Issue Tracker: Visit the GitHub + repository’s [Issues](https://github.com/design-sparx/mantine-analytics-dashboard/issues) tab to report bugs, suggest + enhancements, or seek clarification on aspects of the template. +- Community Interaction: Engage with the template’s community on platforms + like [GitHub Discussions](https://github.com/design-sparx/mantine-analytics-dashboard/discussions) for assistance, + guidance, and insights. + +# Invitation to Explore and Utilize + +The Mantine and Next.js admin dashboard template isn’t just an end; it’s a beginning β€” a starting point for your +creative journey. Whether you’re a seasoned developer seeking a rapid launch or an enthusiast keen on learning modern +development practices, this template is your canvas. + +# Further resources + +- Nextjs - [https://nextjs.org/docs](https://nextjs.org/docs) +- React - [https://react.dev/learn](https://react.dev/learn) +- Mantine - [https://mantine.dev/getting-started/](https://mantine.dev/getting-started/) +- Nextauth - [https://authjs.dev/](https://authjs.dev/) +- Storybook - [https://storybook.js.org/docs/get-started/install](https://storybook.js.org/docs/get-started/install) +- Apexcharts - [https://apexcharts.com/docs/installation/](https://apexcharts.com/docs/installation/) +- Tiptap - [https://tiptap.dev/introduction](https://tiptap.dev/introduction) +- Dndkit - [https://docs.dndkit.com/](https://docs.dndkit.com/) +- Embla carousel - [https://www.embla-carousel.com/get-started/](https://www.embla-carousel.com/get-started/) +- Fullcalendar - [https://fullcalendar.io/docs/getting-started](https://fullcalendar.io/docs/getting-started) +- React simple + maps - [https://www.react-simple-maps.io/docs/getting-started/](https://www.react-simple-maps.io/docs/getting-started/) diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..4c28971 --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,6 @@ +import NextAuth from 'next-auth'; +import { authOptions } from '@/app/lib/auth'; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/app/authProviders/auth0/layout.tsx b/app/authProviders/auth0/layout.tsx new file mode 100644 index 0000000..186e88f --- /dev/null +++ b/app/authProviders/auth0/layout.tsx @@ -0,0 +1,10 @@ +import { ReactNode } from 'react'; +import { Providers } from '@/providers/session'; + +type Auth0LayoutProps = { + children: ReactNode; +}; + +export default function Auth0Layout({ children }: Auth0LayoutProps) { + return {children}; +} diff --git a/app/authProviders/auth0/page.tsx b/app/authProviders/auth0/page.tsx new file mode 100644 index 0000000..edc5c98 --- /dev/null +++ b/app/authProviders/auth0/page.tsx @@ -0,0 +1,117 @@ +'use client'; + +import { signIn, signOut, useSession } from 'next-auth/react'; +import { + Button, + Center, + Container, + Divider, + Group, + Image, + Loader, + Paper, + Stack, + Text, + Title, +} from '@mantine/core'; +import { IconBrandAuth0, IconLogin, IconLogin2 } from '@tabler/icons-react'; + +const ICON_SIZE = 18; + +export default function Page() { + const { data: session, status } = useSession(); + const userEmail = session?.user?.email; + const userImg = session?.user?.image; + const userName = session?.user?.name; + + if (status === 'loading') { + return ( +
+ + + Hang on there... + +
+ ); + } + + if (status === 'authenticated') { + return ( + + + + + Auth0 + + + + + Signed In + + {userName + {userName} + + {userEmail} + + + + + + ); + } + + return ( + <> + <> + Auth0 | DesignSparx + + + + + + + Auth0 + + + + + + Not signed In + + + + + + + + ); +} diff --git a/app/authProviders/clerk/page.tsx b/app/authProviders/clerk/page.tsx new file mode 100644 index 0000000..09382da --- /dev/null +++ b/app/authProviders/clerk/page.tsx @@ -0,0 +1,123 @@ +'use client'; + +import { + ClerkProvider, + SignInButton, + SignUpButton, + SignIn, + SignUp, + SignOutButton, + UserButton, + UserProfile, + ClerkLoading, + ClerkLoaded, +} from '@clerk/nextjs'; +import { + Container, + Image, + Stack, + Divider, + Group, + Title, + Text, + Paper, + Button, + useMantineColorScheme, + useMantineTheme, + parseThemeColor, + Select, + Flex, +} from '@mantine/core'; +import { dark, neobrutalism, shadesOfPurple } from '@clerk/themes'; +import { useEffect, useState } from 'react'; +import { IconLogin, IconLogin2, IconUserCircle } from '@tabler/icons-react'; + +const ICON_SIZE = 18; + +function Home() { + const { colorScheme } = useMantineColorScheme(); + const theme = useMantineTheme(); + const parsedThemeColor = parseThemeColor({ + color: theme.primaryColor, + theme, + }); + const [clerkTheme, setClerkTheme] = useState(''); + + const clerkAppearanceOptions: any = { + variables: { colorPrimary: parsedThemeColor.value }, + }; + + useEffect(() => { + if (clerkTheme === 'dark') { + clerkAppearanceOptions['baseTheme'] = dark; + } else if (clerkTheme === 'neobrutalism') { + clerkAppearanceOptions['baseTheme'] = neobrutalism; + } else if (clerkTheme === 'purpleShades') { + clerkAppearanceOptions['baseTheme'] = shadesOfPurple; + } + }, [clerkTheme]); + + return ( + <> + <> + Clerk | DesignSparx + + + + + + + + + toggle weekends + + +
+

All Events ({currentEvents.length})

+
    {currentEvents.map(renderSidebarEvent)}
+
+ + ); + }; + + return ( + <> + <> + CalendarView | DesignSparx + + + + + + + + + + + + ); +} + +export default Calendar; diff --git a/app/dashboard/analytics/page.tsx b/app/dashboard/analytics/page.tsx new file mode 100644 index 0000000..b7045ed --- /dev/null +++ b/app/dashboard/analytics/page.tsx @@ -0,0 +1,122 @@ +'use client'; + +import { + Container, + Grid, + PaperProps, + rem, + SimpleGrid, + Skeleton, + Stack, + useMantineTheme, +} from '@mantine/core'; +import { + ErrorAlert, + LanguageTable, + MapChart, + MobileDesktopChart, + PageHeader, + SalesChart, + StatsCard, + TrafficTable, +} from '@/components'; +import { useFetchData } from '@/hooks'; + +const PRIMARY_COL_HEIGHT = rem(300); + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + style: { height: '100%' }, +}; + +function Page() { + const theme = useMantineTheme(); + const SECONDARY_COL_HEIGHT = `calc(${PRIMARY_COL_HEIGHT} / 2 - var(--mantine-spacing-md) / 2)`; + const { + data: statsData, + error: statsError, + loading: statsLoading, + } = useFetchData('/mocks/StatsGrid.json'); + const { + data: languagesData, + error: languageError, + loading: languageLoading, + } = useFetchData('/mocks/Languages.json'); + const { + data: trafficData, + error: trafficError, + loading: trafficLoading, + } = useFetchData('/mocks/Traffic.json'); + + return ( + <> + <> + Analytics Dashboard | DesignSparx + + + + + + + {statsError ? ( + + ) : ( + + {statsLoading + ? Array.from({ length: 4 }).map((o, i) => ( + + )) + : statsData?.data?.map((s: any) => ( + + ))} + + )} + + + + + + + + + + + + + + + + + + + + ); +} + +export default Page; diff --git a/app/dashboard/default/page.tsx b/app/dashboard/default/page.tsx new file mode 100644 index 0000000..5380e05 --- /dev/null +++ b/app/dashboard/default/page.tsx @@ -0,0 +1,102 @@ +'use client'; + +import { + Button, + Container, + Grid, + Group, + Paper, + PaperProps, + Stack, + Text, +} from '@mantine/core'; +import { IconChevronRight } from '@tabler/icons-react'; +import { + MobileDesktopChart, + PageHeader, + ProjectsTable, + RevenueChart, + SalesChart, + StatsGrid, +} from '@/components'; +import Link from 'next/link'; +import { PATH_TASKS } from '@/routes'; +import { useFetchData } from '@/hooks'; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + style: { height: '100%' }, +}; + +function Page() { + const { + data: projectsData, + error: projectsError, + loading: projectsLoading, + } = useFetchData('/mocks/Projects.json'); + const { + data: statsData, + error: statsError, + loading: statsLoading, + } = useFetchData('/mocks/StatsGrid.json'); + + return ( + <> + <> + Default Dashboard | DesignSparx + + + + + + + + + + + + + + + + + + + + + Tasks + + + + + + + + + + + ); +} + +export default Page; diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx new file mode 100644 index 0000000..22dc793 --- /dev/null +++ b/app/dashboard/layout.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { AppShell, Container, rem, useMantineTheme } from '@mantine/core'; +import { ReactNode, useState } from 'react'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; +import AppMain from '@/components/AppMain'; +import Navigation from '@/components/Navigation'; +import HeaderNav from '@/components/HeaderNav'; +import FooterNav from '@/components/FooterNav'; + +type Props = { + children: ReactNode; +}; + +function CalendarLayout({ children }: Props) { + const theme = useMantineTheme(); + const [opened, setOpened] = useState(false); + const [themeOpened, { open: themeOpen, close: themeClose }] = + useDisclosure(false); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + setOpened((o) => !o)} + desktopOpened={desktopOpened} + mobileOpened={mobileOpened} + toggleDesktop={toggleDesktop} + toggleMobile={toggleMobile} + /> + + + + setOpened(false)} /> + + + {children} + + + + + + + + ); +} + +export default CalendarLayout; diff --git a/app/dashboard/saas/page.tsx b/app/dashboard/saas/page.tsx new file mode 100644 index 0000000..793f9cc --- /dev/null +++ b/app/dashboard/saas/page.tsx @@ -0,0 +1,98 @@ +'use client'; + +import { + Button, + Container, + Grid, + Group, + Paper, + PaperProps, + Stack, + Text, +} from '@mantine/core'; +import { IconChevronRight } from '@tabler/icons-react'; +import { + MapChart, + PageHeader, + ProjectsTable, + RevenueChart, + SalesChart, + StatsGrid, +} from '@/components'; +import { useFetchData } from '@/hooks'; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + style: { height: '100%' }, +}; + +function Page() { + const { + data: statsData, + error: statsError, + loading: statsLoading, + } = useFetchData('/mocks/StatsGrid.json'); + const { + data: projectsData, + error: projectsError, + loading: projectsLoading, + } = useFetchData('/mocks/Projects.json'); + + return ( + <> + <> + Sass Dashboard | DesignSparx + + + + + + + + + + + + + + + + + + + + + Tasks + + + + + + + + + + + ); +} + +export default Page; diff --git a/app/error.module.css b/app/error.module.css new file mode 100644 index 0000000..ffb9a3f --- /dev/null +++ b/app/error.module.css @@ -0,0 +1,30 @@ +.root { + padding: rem(80px) 0; +} + +.label { + text-align: center; + font-weight: 900; + font-size: rem(220px); + line-height: 1; + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(120px); + } +} + +.title { + text-align: center; + font-weight: 900; + font-size: rem(38px); + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(32px); + } +} + +.description { + max-width: rem(500px); + margin: auto; +} diff --git a/app/error.tsx b/app/error.tsx new file mode 100644 index 0000000..dbe640e --- /dev/null +++ b/app/error.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { useEffect } from 'react'; +import { + Button, + Center, + Group, + Stack, + Text, + Title, + useMantineTheme, +} from '@mantine/core'; +import { useRouter } from 'next/navigation'; +import { IconHome2, IconRefresh } from '@tabler/icons-react'; +import classes from './error.module.css'; + +function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + const router = useRouter(); + const theme = useMantineTheme(); + + useEffect(() => { + // Log the error to an error reporting service + console.error(error); + }, [error]); + + return ( + <> + <> + Server Error | DesignSparx + + +
+ +
400
+ Sorry, unexpected error.. + + {error.toString()} + + + + + +
+
+ + ); +} + +export default Error; diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..7ca559b --- /dev/null +++ b/app/globals.css @@ -0,0 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); + +html, +body { +} diff --git a/app/invoices/details/[id]/page.tsx b/app/invoices/details/[id]/page.tsx new file mode 100644 index 0000000..bc6c4a0 --- /dev/null +++ b/app/invoices/details/[id]/page.tsx @@ -0,0 +1,63 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { Anchor, Container, PaperProps, Stack } from '@mantine/core'; +import { InvoiceDetailsCard, PageHeader } from '@/components'; +import { PATH_DASHBOARD, PATH_INVOICES } from '@/routes'; +import { Invoices } from '@/types'; +import { useFetchData } from '@/hooks'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Invoices', href: PATH_INVOICES.invoices.all }, + { title: 'Details', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', +}; + +function InvoiceDetails({ params }: { params: { id: string } }) { + const [selectedData, setSelectedData] = useState(); + const { + data: invoicesData, + loading: invoicesLoading, + error: invoicesError, + } = useFetchData('mocks/Invoices.json'); + + useEffect(() => { + setSelectedData(invoicesData.find((_: Invoices) => _.id === params.id)); + }, [invoicesData, params]); + + return ( + <> + <> + + Invoice - {selectedData ? selectedData?.id : 'No invoice found'} | + DesignSparx + + + + + + + + + + + ); +} + +export default InvoiceDetails; diff --git a/app/invoices/details/page.tsx b/app/invoices/details/page.tsx new file mode 100644 index 0000000..f936410 --- /dev/null +++ b/app/invoices/details/page.tsx @@ -0,0 +1,71 @@ +'use client'; + +import { Anchor, Container, PaperProps, Stack } from '@mantine/core'; +import { InvoiceDetailsCard, PageHeader } from '@/components'; +import { PATH_DASHBOARD, PATH_INVOICES } from '@/routes'; +import { Metadata } from 'next'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Invoices', href: PATH_INVOICES.invoices.all }, + { title: 'Details', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const sampleData = { + id: '8677a3e2-dde3-4d04-8edd-9d0bcf178f89', + full_name: 'Dannie MacTrustie', + email: 'atysack2r@washingtonpost.com', + address: '5160 Iowa Point', + country: 'China', + status: 'approved', + amount: 6221.88, + issue_date: '7/12/2022', + description: + 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\n\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', + client_email: 'atysack2r@illinois.edu', + client_address: '13 Loeprich Point', + client_country: 'Russia', + client_name: 'Alayne Tysack', + client_company: 'Raynor and Sons', +}; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', +}; + +const metadata: Metadata = { + title: 'Sample Invoice | DesignSparx', + description: + 'Explore our versatile dashboard website template featuring a stunning array of themes and meticulously crafted components. Elevate your web project with seamless integration, customizable themes, and a rich variety of components for a dynamic user experience. Effortlessly bring your data to life with our intuitive dashboard template, designed to streamline development and captivate users. Discover endless possibilities in design and functionality today!', +}; + +function SampleInvoiceDetails() { + return ( + <> + <> + Sample Invoice Details | DesignSparx + + + + + + + + + + ); +} + +export default SampleInvoiceDetails; diff --git a/app/invoices/layout.tsx b/app/invoices/layout.tsx new file mode 100644 index 0000000..22dc793 --- /dev/null +++ b/app/invoices/layout.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { AppShell, Container, rem, useMantineTheme } from '@mantine/core'; +import { ReactNode, useState } from 'react'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; +import AppMain from '@/components/AppMain'; +import Navigation from '@/components/Navigation'; +import HeaderNav from '@/components/HeaderNav'; +import FooterNav from '@/components/FooterNav'; + +type Props = { + children: ReactNode; +}; + +function CalendarLayout({ children }: Props) { + const theme = useMantineTheme(); + const [opened, setOpened] = useState(false); + const [themeOpened, { open: themeOpen, close: themeClose }] = + useDisclosure(false); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + setOpened((o) => !o)} + desktopOpened={desktopOpened} + mobileOpened={mobileOpened} + toggleDesktop={toggleDesktop} + toggleMobile={toggleMobile} + /> + + + + setOpened(false)} /> + + + {children} + + + + + + + + ); +} + +export default CalendarLayout; diff --git a/app/invoices/list/page.tsx b/app/invoices/list/page.tsx new file mode 100644 index 0000000..9ec7749 --- /dev/null +++ b/app/invoices/list/page.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { + ActionIcon, + Anchor, + Container, + Group, + Paper, + PaperProps, + Stack, + Text, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { InvoicesTable, PageHeader } from '@/components'; +import InvoicesData from '@/public/mocks/Invoices.json'; +import { IconDotsVertical } from '@tabler/icons-react'; +import { Metadata } from 'next'; +import { useFetchData } from '@/hooks'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Invoices', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', +}; + +function Page() { + const { + data: invoicesData, + loading: invoicesLoading, + error: invoicesError, + } = useFetchData('/mocks/Invoices.json'); + + return ( + <> + <> + Invoices | DesignSparx + + + + + + + + + Invoices + + + + + + + + + + + ); +} + +export default Page; diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..66d7ad2 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { ColorSchemeScript, MantineProvider } from '@mantine/core'; +import { ModalsProvider } from '@mantine/modals'; +import { Notifications } from '@mantine/notifications'; +import { Open_Sans } from 'next/font/google'; +import { myTheme } from '@/theme'; +import '@mantine/core/styles.css'; +import '@mantine/dates/styles.css'; +import '@mantine/tiptap/styles.css'; +import '@mantine/carousel/styles.css'; +import '@mantine/notifications/styles.css'; +import 'mantine-datatable/styles.layer.css'; +import './globals.css'; + +// If loading a variable font, you don't need to specify the font weight +const openSans = Open_Sans({ + subsets: ['latin'], + display: 'swap', +}); +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + DesignSparx - Nextjs Mantine Admin Dashboard Template + + + + + + + + + + + + {children} + + + + ); +} diff --git a/app/lib/auth.ts b/app/lib/auth.ts new file mode 100644 index 0000000..4ce2af6 --- /dev/null +++ b/app/lib/auth.ts @@ -0,0 +1,21 @@ +import Auth0 from 'next-auth/providers/auth0'; +import { NextAuthOptions } from 'next-auth'; + +const { + AUTH0_CLIENT_ID = '', + AUTH0_CLIENT_SECRET = '', + AUTH0_DOMAIN = '', + AUTH0_NEXT_SECRET = '', + AUTH0_ISSUER_BASE_URL = '', +} = process.env; + +export const authOptions: NextAuthOptions = { + secret: AUTH0_NEXT_SECRET, + providers: [ + Auth0({ + clientId: AUTH0_CLIENT_ID, + clientSecret: AUTH0_CLIENT_SECRET, + issuer: AUTH0_ISSUER_BASE_URL, + }), + ], +}; diff --git a/app/loading.tsx b/app/loading.tsx new file mode 100644 index 0000000..b8c1c51 --- /dev/null +++ b/app/loading.tsx @@ -0,0 +1,12 @@ +import { Center, Loader, Text, Stack } from '@mantine/core'; + +export default function Loading() { + return ( +
+ + + Hang in there... + +
+ ); +} diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..f226121 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,75 @@ +'use client'; + +import { + Button, + Center, + Group, + Stack, + Text, + Title, + useMantineTheme, +} from '@mantine/core'; +import Link from 'next/link'; +import { PATH_DASHBOARD } from '@/routes'; +import { IconChevronLeft, IconHome2 } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import classes from './error.module.css'; + +function Error404() { + const router = useRouter(); + const theme = useMantineTheme(); + + return ( + <> + <> + Page Not Found | DesignSparx + + +
+ +
404
+ + You have found a secret place. + + + Unfortunately, this is only a 404 page. You may have mistyped the + address, or the page has been moved to another URL. + + + + + +
+
+ + ); +} + +export default Error404; diff --git a/app/orders/layout.tsx b/app/orders/layout.tsx new file mode 100644 index 0000000..22dc793 --- /dev/null +++ b/app/orders/layout.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { AppShell, Container, rem, useMantineTheme } from '@mantine/core'; +import { ReactNode, useState } from 'react'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; +import AppMain from '@/components/AppMain'; +import Navigation from '@/components/Navigation'; +import HeaderNav from '@/components/HeaderNav'; +import FooterNav from '@/components/FooterNav'; + +type Props = { + children: ReactNode; +}; + +function CalendarLayout({ children }: Props) { + const theme = useMantineTheme(); + const [opened, setOpened] = useState(false); + const [themeOpened, { open: themeOpen, close: themeClose }] = + useDisclosure(false); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + setOpened((o) => !o)} + desktopOpened={desktopOpened} + mobileOpened={mobileOpened} + toggleDesktop={toggleDesktop} + toggleMobile={toggleMobile} + /> + + + + setOpened(false)} /> + + + {children} + + + + + + + + ); +} + +export default CalendarLayout; diff --git a/app/orders/page.tsx b/app/orders/page.tsx new file mode 100644 index 0000000..dfd0d3a --- /dev/null +++ b/app/orders/page.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { + ActionIcon, + Anchor, + Container, + Group, + Paper, + PaperProps, + Stack, + Text, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { OrdersTable, PageHeader } from '@/components'; +import { IconDotsVertical } from '@tabler/icons-react'; +import { useFetchData } from '@/hooks'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Orders', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', +}; + +function Page() { + const { + data: ordersData, + loading: ordersLoading, + error: ordersError, + } = useFetchData('/mocks/Orders.json'); + + return ( + <> + <> + Orders | DesignSparx + + + + + + + + + Orders + + + + + + + + + + + ); +} + +export default Page; diff --git a/app/page.module.css b/app/page.module.css new file mode 100644 index 0000000..3b6b4df --- /dev/null +++ b/app/page.module.css @@ -0,0 +1,70 @@ +.hero { + background-color: var(--mantine-color-black); + color: var(--mantine-color-white); + padding: calc(var(--mantine-spacing-xl) * 4); + + @media (max-width: $mantine-breakpoint-sm) { + padding-top: var(--mantine-spacing-xl); + } +} + +.title { + font-weight: 800; + font-size: rem(40); + letter-spacing: rem(-1); + margin-bottom: var(--mantine-spacing-xs); + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(28); + text-align: left; + } +} + +.highlight { + color: var(--mantine-primary-color-filled); +} + +.stackControl { + text-transform: capitalize; + background-color: var(--mantine-color-dark-6); + color: var(--mantine-color-white); + padding: rem(6) rem(10); + border-radius: var(--mantine-radius-md); +} + +.variantImg { + max-height: rem(280); + width: 100%; + object-fit: contain; +} + +.variantTitle { + text-align: center; + margin-top: var(--mantine-spacing-md); + font-weight: 600; +} + +.paper { + height: 100%; + background-color: transparent; +} + +.image { + height: 100%; + background-color: transparent; + + @mixin hover { + box-shadow: var(--mantine-shadow-lg); + transition: all ease 150ms; + transform: scale(1.03); + } +} + +.statsTitle { + color: var(--mantine-primary-color-filled); +} + +.contactPaper { + background-color: transparent; + text-align: center; +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..9619517 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,381 @@ +'use client'; + +import { + Badge, + Box, + BoxProps, + Button, + Center, + Container, + Flex, + Grid, + Group, + Image, + Paper, + PaperProps, + SimpleGrid, + Spoiler, + Stack, + Text, + ThemeIcon, + ThemeIconProps, + Title, + Tooltip, + UnstyledButton, +} from '@mantine/core'; +import Link from 'next/link'; +import { PATH_DASHBOARD, PATH_DOCS } from '@/routes'; +import { + IconAdjustmentsHorizontal, + IconArrowRight, + IconBook, + IconColorSwatch, + IconDevices, + IconFolderCode, + IconScaleOutline, + IconSettingsCog, +} from '@tabler/icons-react'; +import CountUp from 'react-countup'; +import { useMediaQuery } from '@mantine/hooks'; +import GuestLayout from '@/layout/Guest'; +import classes from './page.module.css'; + +const TECH_STACK = [ + { title: 'nextjs', version: '14.0.2', href: 'https://nextjs.org/' }, + { title: 'react', version: '18.2.0', href: 'https://react.dev/' }, + { + title: 'typescript', + version: '5.1.6', + href: 'https://www.typescriptlang.org/', + }, + { title: 'mantine', version: '7.2.2', href: 'https://mantine.dev/' }, + { + title: 'tabler icons', + version: '2.40.0', + href: 'https://tabler-icons.io/', + }, + { title: 'tiptap', version: '2.1.12', href: 'https://tiptap.dev/' }, + { title: 'apexcharts', version: '3.44.0', href: 'https://apexcharts.com/' }, + { title: 'dayjs', version: '1.11.10', href: 'https://day.js.org/' }, + { title: 'fullcalendar', version: '6.1.8', href: 'https://fullcalendar.io/' }, + { + title: 'emotion', + version: '11.11.1', + href: 'https://emotion.sh/docs/introduction', + }, + { title: 'dnd-kit', version: '6.0.8', href: 'https://dndkit.com/' }, + { + title: 'embla-carousel', + version: '8.0.0', + href: 'https://www.embla-carousel.com/', + }, + { + title: 'mantine datatable', + version: '7.1.7', + href: 'https://icflorescu.github.io/mantine-datatable', + }, + { title: 'lodash', version: '4.17.21', href: 'https://lodash.com/' }, + { + title: 'react simple maps', + version: '3.0.0', + href: 'https://www.react-simple-maps.io/', + }, +]; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + className: classes.paper, +}; + +const THEME_ICON_PROPS: Omit = { + variant: 'light', + size: 48, +}; + +const IMAGE_PAPER_PROPS: PaperProps = { + p: 'md', + className: classes.image, +}; + +export default function Home() { + const tablet_match = useMediaQuery('(max-width: 768px)'); + + const BOX_PROPS: BoxProps = { + mt: 120, + pb: 80, + px: tablet_match ? 20 : 48, + }; + + return ( + <> + <> + DesignSparx | Website UI Kit + + + + + + + + + + Build like a Pro + + The simplest and fastest way to build your next{' '} + <Text component="span" inherit className={classes.highlight}> + Mantine UI{' '} + </Text> + &{' '} + <Text component="span" inherit className={classes.highlight}> + Nextjs{' '} + </Text> + dashboard or app. + + + This template comes with hundreds of UI elements, forms, + tables, charts, pages and icons that helps you to create your + web apps or applications faster. + + + + + + + Tech Stack: + + + {TECH_STACK.map((t) => ( + + + {t.title} + + + ))} + + + + + + + / + + + + + + + Carefully crafted components ready to use in your project + + + + + + <CountUp end={50} />+ + + Beautifully coded page examples + + + + + + <CountUp end={100} />+ + + Components and widgets + + + + + + Optimized to work for most devices + + + + + + Customize it to meet your brand's identity + + + + + + + + Default variant + + + + Pink variant + + + + Orange variant + + + + Green variant + + + + Purple variant + + + + Dark variant + + + + + + + Key features + + + Quick helps you build beautiful websites that stand out and + automatically adapt to your style. + + + + + + + + Modular + + + All components are built to be used in any combination. + + + + + + + + Responsive + + + Quick is optimized to work for most devices. + + + + + + + + Scalable + + + Remain consistent while developing new features. + + + + + + + + Customizable + + + Change a few variables and the whole theme adapts. + + + +
+ +
+
+
+ + + + For any queries? + + + + +
+ + ); +} diff --git a/app/pages/blank/page.tsx b/app/pages/blank/page.tsx new file mode 100644 index 0000000..9884139 --- /dev/null +++ b/app/pages/blank/page.tsx @@ -0,0 +1,56 @@ +'use client'; + +import { + Anchor, + Container, + Paper, + PaperProps, + Stack, + Text, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { PageHeader, Surface } from '@/components'; +import { Metadata } from 'next'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Pages', href: '#' }, + { title: 'Blank', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', +}; + +function Pricing() { + return ( + <> + <> + Blank page | DesignSparx + + + + + + + + Empty card header + + Empty card text + + + + + ); +} + +export default Pricing; diff --git a/app/pages/chat/page.module.css b/app/pages/chat/page.module.css new file mode 100644 index 0000000..4353503 --- /dev/null +++ b/app/pages/chat/page.module.css @@ -0,0 +1,54 @@ +.chatItems { + @mixin light { + background-color: var(--mantine-color-gray-1); + border-left: rem(1px) solid var(--mantine-color-gray-3); + } + + @mixin dark { + background-color: var(--mantine-color-dark-6); + border-left: rem(1px) solid var(--mantine-color-gray-7); + } + + @media (max-width: $mantine-breakpoint-md) { + @mixin light { + border-left: rem(1px) solid var(--mantine-color-gray-3); + } + + @mixin dark { + border-left: rem(1px) solid var(--mantine-color-gray-7); + } + } +} + +.chatHeader { + padding: var(--mantine-spacing-sm) var(--mantine-spacing-md); + border-top-right-radius: var(--mantine-radius-default); + border-bottom: 1px solid + light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-0)); + + @mixin light { + background-color: var(--mantine-color-white); + border-bottom: rem(1px) solid var(--mantine-color-gray-3); + } + + @mixin dark { + background-color: var(--mantine-color-black); + border-bottom: rem(1px) solid var(--mantine-color-gray-7); + } +} + +.user { + border-radius: var(--mantine-radius-default); +} + +.replyBox { + padding: var(--mantine-spacing-sm); + + @mixin light { + background-color: var(--mantine-color-white); + } + + @mixin dark { + background-color: var(--mantine-color-black); + } +} diff --git a/app/pages/chat/page.tsx b/app/pages/chat/page.tsx new file mode 100644 index 0000000..84e36f4 --- /dev/null +++ b/app/pages/chat/page.tsx @@ -0,0 +1,281 @@ +'use client'; + +import { + ActionIcon, + Anchor, + Box, + Container, + Divider, + Flex, + Grid, + Paper, + PaperProps, + rem, + ScrollArea, + Skeleton, + Stack, + TextInput, + Tooltip, + useMantineTheme, +} from '@mantine/core'; +import { Link, RichTextEditor } from '@mantine/tiptap'; +import { BubbleMenu, useEditor } from '@tiptap/react'; +import StarterKit from '@tiptap/starter-kit'; +import Placeholder from '@tiptap/extension-placeholder'; +import { PATH_DASHBOARD } from '@/routes'; +import { + ChatItem, + ChatsList, + ErrorAlert, + PageHeader, + Surface, + UserButton, +} from '@/components'; +import { IconDotsVertical, IconSearch, IconSend } from '@tabler/icons-react'; +import { useColorScheme, useMediaQuery } from '@mantine/hooks'; +import { Carousel } from '@mantine/carousel'; +import ChatsListData from '@/public/mocks/ChatsList.json'; +import ChatItemsData from '@/public/mocks/ChatItems.json'; +import UserProfileData from '@/public/mocks/UserProfile.json'; +import { useFetchData } from '@/hooks'; + +import classes from './page.module.css'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Pages', href: '#' }, + { title: 'Chat', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const ICON_SIZE = 16; + +const PAPER_PROPS: PaperProps = { + shadow: 'md', + radius: 'md', +}; + +function Chat() { + const theme = useMantineTheme(); + const colorScheme = useColorScheme(); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const editor = useEditor({ + extensions: [ + StarterKit, + Link, + Placeholder.configure({ placeholder: 'Type your message' }), + ], + content: '

Select some text to see a bubble menu

', + }); + const { + data: chatsListData, + loading: chatsListLoading, + error: chatsListError, + } = useFetchData('/mocks/ChatsList.json'); + const { + data: chatItemsData, + loading: chatsItemsLoading, + error: chatsItemsError, + } = useFetchData('/mocks/ChatItems.json'); + + return ( + <> + <> + Chat | DesignSparx + + + + + + + + + + + } + /> + + {tablet_match ? ( + <> + + {chatsListLoading ? ( + Array.from({ length: 6 }).map((o, i) => ( + + + + )) + ) : chatsListError ? ( + + ) : ( + chatsListData.length > 0 && + chatsListData.map((c: any) => ( + + + + )) + )} + + + + ) : ( + + {chatsListLoading ? ( + Array.from({ length: 6 }).map((o, i) => ( + + + + + )) + ) : chatsListError ? ( + + ) : ( + chatsListData.length > 0 && + chatsListData.map((c: any) => ( + + )) + )} + + )} + + + + + + + + + + + + + + + + + + + + + + {chatsItemsError ? ( + + ) : ( + chatItemsData.length > 0 && + chatItemsData.map((c: any) => ( + + )) + )} + + + + + + + + {editor && ( + + + + + + + + )} + + + + + + + + + + + + + + + + + + ); +} + +export default Chat; diff --git a/app/pages/layout.tsx b/app/pages/layout.tsx new file mode 100644 index 0000000..22dc793 --- /dev/null +++ b/app/pages/layout.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { AppShell, Container, rem, useMantineTheme } from '@mantine/core'; +import { ReactNode, useState } from 'react'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; +import AppMain from '@/components/AppMain'; +import Navigation from '@/components/Navigation'; +import HeaderNav from '@/components/HeaderNav'; +import FooterNav from '@/components/FooterNav'; + +type Props = { + children: ReactNode; +}; + +function CalendarLayout({ children }: Props) { + const theme = useMantineTheme(); + const [opened, setOpened] = useState(false); + const [themeOpened, { open: themeOpen, close: themeClose }] = + useDisclosure(false); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + setOpened((o) => !o)} + desktopOpened={desktopOpened} + mobileOpened={mobileOpened} + toggleDesktop={toggleDesktop} + toggleMobile={toggleMobile} + /> + + + + setOpened(false)} /> + + + {children} + + + + + + + + ); +} + +export default CalendarLayout; diff --git a/app/pages/pricing/page.tsx b/app/pages/pricing/page.tsx new file mode 100644 index 0000000..227f0b9 --- /dev/null +++ b/app/pages/pricing/page.tsx @@ -0,0 +1,161 @@ +'use client'; + +import React, { useState } from 'react'; +import { + Anchor, + Button, + Container, + Flex, + Paper, + PaperProps, + SimpleGrid, + Stack, + Switch, + Text, + Title, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { Faqs, PageHeader, PricingCard, Surface } from '@/components'; +import { IconChevronRight } from '@tabler/icons-react'; +import { Metadata } from 'next'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Pages', href: '#' }, + { title: 'Pricing', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const ICON_SIZE = 16; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + style: { height: '100%' }, +}; + +const PRICING = [ + { + tier: 'basic', + price: { + month: 0, + year: 0, + }, + features: ['Rich landing pages', '100+ components'], + preferred: false, + actionText: 'start for free', + description: 'All the basics for starting a new business', + }, + { + tier: 'standard', + price: { + month: 25, + year: 45, + }, + features: [ + 'Rich landing pages', + '100+ components', + 'Flexible licensing', + 'Speedy build tooling', + '6 months free support', + ], + preferred: true, + actionText: 'start with standard', + description: 'Everything you need for a growing business', + }, + { + tier: 'premium', + price: { + month: 40, + year: 70, + }, + features: [ + 'Rich landing pages', + '100+ components', + 'Flexible licensing', + 'Speedy build tooling', + '6 months free support', + '256-bit encryption', + 'Guaranteed 100% uptime', + 'Unlimited users', + ], + preferred: false, + actionText: 'start with premium', + description: 'Advanced features for scaling your business', + }, +]; + +function Pricing() { + const [checked, setChecked] = useState(false); + const pricingItems = PRICING.map((p) => ( + + )); + + return ( + <> + <> + Pricing | DesignSparx + + + + + + + + + Simple, fair pricing. + + + All types of businesses need access to development resources, so + we give you the option to decide how much you need to use. + + + Annual + setChecked(event.currentTarget.checked)} + /> + Monthly + + + + + {pricingItems} + + + + + + + Still have questions? + + + + + + + ); +} + +export default Pricing; diff --git a/app/pages/profile/page.module.css b/app/pages/profile/page.module.css new file mode 100644 index 0000000..ca91520 --- /dev/null +++ b/app/pages/profile/page.module.css @@ -0,0 +1,11 @@ +.socialLink { + border-radius: var(--mantine-radius-default); + padding: rem(6px) rem(8px); + + @mixin hover { + transition: all ease 150ms; + background-color: var(--mantine-color-gray-3); + color: var(--mantine-color-black); + text-decoration: none; + } +} diff --git a/app/pages/profile/page.tsx b/app/pages/profile/page.tsx new file mode 100644 index 0000000..1662a45 --- /dev/null +++ b/app/pages/profile/page.tsx @@ -0,0 +1,239 @@ +'use client'; + +import { + ActionIcon, + Anchor, + Badge, + Container, + Flex, + Grid, + Group, + Paper, + PaperProps, + SimpleGrid, + Stack, + Text, + UnstyledButton, + useMantineTheme, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { + PageHeader, + ProfileStatsCard, + ProjectsTable, + RevenueChart, + Surface, + UserProfileCard, +} from '@/components'; +import { + IconBrandFacebook, + IconBrandGithub, + IconBrandLinkedin, + IconBrandTwitter, + IconBusinessplan, + IconCoins, + IconDotsVertical, + IconHome, + IconListCheck, + IconMapPinFilled, +} from '@tabler/icons-react'; +import UserData from '@/public/mocks/UserProfile.json'; +import classes from './page.module.css'; +import { useFetchData } from '@/hooks'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Pages', href: '#' }, + { title: 'Profile', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const ICON_SIZE = 16; + +const skills = [ + 'React', + 'Mantine', + 'Figma', + 'Bootstrap', + 'Typescript', + 'Sass/SCSS', +]; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + style: { height: '100%' }, +}; + +function Profile() { + const theme = useMantineTheme(); + const { + data: projectsData, + loading: projectsLoading, + error: projectsError, + } = useFetchData('/mocks/Projects.json'); + const linkProps = { + target: '_blank', + className: classes.socialLink, + }; + + return ( + <> + <> + Profile | DesignSparx + + + + + + + + + + + + Skills + + + {skills.map((s) => ( + + {s} + + ))} + + + + + + About + + + + Lives in Nairobi, Kenya + + + + Works at Company ABC + + + + + + + Social + + + + + Facebook + + + + + + Twitter + + + + + + LinkedIn + + + + + + Github + + + + + + + + + + + + + + + + + + Projects + + + + + + + + + + + + + + ); +} + +export default Profile; diff --git a/app/pages/settings/page.tsx b/app/pages/settings/page.tsx new file mode 100644 index 0000000..be25221 --- /dev/null +++ b/app/pages/settings/page.tsx @@ -0,0 +1,197 @@ +'use client'; + +import React, { useState } from 'react'; +import { + Anchor, + Box, + Button, + Container, + FileButton, + Grid, + Group, + Image, + Paper, + PaperProps, + Stack, + Text, + TextInput, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { useForm } from '@mantine/form'; +import { IconCloudUpload, IconDeviceFloppy } from '@tabler/icons-react'; +import { PageHeader, Surface, TextEditor } from '@/components'; +import { Metadata } from 'next'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Pages', href: '#' }, + { title: 'Settings', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const ICON_SIZE = 16; + +const PAPER_PROPS: PaperProps = { + p: 'md', + shadow: 'md', + radius: 'md', + style: { height: '100%' }, +}; + +const BIO = + 'A dynamic software engineering graduate from Nairobi, Kenya with 5+ years of experience. Passionate about turning creative sparks into seamless applications through technological experimentation. Experienced in crafting intuitive solutions and translating innovative concepts into user-friendly applications. Thrives on transforming the way we experience technology, one line of code at a time.\n' + + '\n' + + 'Enthusiastic pioneer, constantly seeking the next big thing in tech. Eager to apply my passion and skills at Alternate Limited to bring ideas to life.'; + +function Settings() { + const [file, setFile] = useState(null); + + const accountForm = useForm({ + initialValues: { + username: 'kelvinkiprop', + biograghy: + 'A dynamic software engineering graduate from Nairobi, Kenya with 5+ years of experience. Passionate about turning creative sparks into seamless applications through technological experimentation. Experienced in crafting intuitive solutions and translating innovative concepts into user-friendly applications. Thrives on transforming the way we experience technology, one line of code at a time.\n' + + '\n' + + 'Enthusiastic pioneer, constantly seeking the next big thing in tech. Eager to apply my passion and skills at Alternate Limited to bring ideas to life.', + }, + }); + + const accountInfoForm = useForm({ + initialValues: { + firstname: 'kelvin', + lastname: 'kiprop', + email: 'kelvin.kiprop96@gmail.com', + address: '', + apartment: '', + city: '', + state: '', + zip: '', + }, + }); + + return ( + <> + <> + Settings | DesignSparx + + + + + + + + User information + + + + + + + + + + + + + + {(props) => ( + + )} + + + For best results, use an image at least 128px by 128px in + .jpg format + + + + + + + + + Account information + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default Settings; diff --git a/app/projects/layout.tsx b/app/projects/layout.tsx new file mode 100644 index 0000000..22dc793 --- /dev/null +++ b/app/projects/layout.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { AppShell, Container, rem, useMantineTheme } from '@mantine/core'; +import { ReactNode, useState } from 'react'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; +import AppMain from '@/components/AppMain'; +import Navigation from '@/components/Navigation'; +import HeaderNav from '@/components/HeaderNav'; +import FooterNav from '@/components/FooterNav'; + +type Props = { + children: ReactNode; +}; + +function CalendarLayout({ children }: Props) { + const theme = useMantineTheme(); + const [opened, setOpened] = useState(false); + const [themeOpened, { open: themeOpen, close: themeClose }] = + useDisclosure(false); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + setOpened((o) => !o)} + desktopOpened={desktopOpened} + mobileOpened={mobileOpened} + toggleDesktop={toggleDesktop} + toggleMobile={toggleMobile} + /> + + + + setOpened(false)} /> + + + {children} + + + + + + + + ); +} + +export default CalendarLayout; diff --git a/app/projects/page.tsx b/app/projects/page.tsx new file mode 100644 index 0000000..a32c612 --- /dev/null +++ b/app/projects/page.tsx @@ -0,0 +1,82 @@ +'use client'; + +import { + Anchor, + CardProps, + Container, + SimpleGrid, + Skeleton, + Stack, +} from '@mantine/core'; +import { PATH_DASHBOARD } from '@/routes'; +import { ErrorAlert, PageHeader, ProjectsCard } from '@/components'; +import { useFetchData } from '@/hooks'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Projects', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +const ICON_SIZE = 18; + +const CARD_PROPS: Omit = { + p: 'md', + shadow: 'md', + radius: 'md', +}; + +function Projects() { + const { + data: projectsData, + loading: projectsLoading, + error: projectsError, + } = useFetchData('/mocks/Projects2.json'); + const projectItems = projectsData.map((p: any) => ( + + )); + + return ( + <> + <> + Projects | DesignSparx + + + + + + {projectsError ? ( + + ) : ( + + {projectsLoading + ? Array.from({ length: 8 }).map((o, i) => ( + + )) + : projectItems} + + )} + + + + ); +} + +export default Projects; diff --git a/app/tasks/layout.tsx b/app/tasks/layout.tsx new file mode 100644 index 0000000..22dc793 --- /dev/null +++ b/app/tasks/layout.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { AppShell, Container, rem, useMantineTheme } from '@mantine/core'; +import { ReactNode, useState } from 'react'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; +import AppMain from '@/components/AppMain'; +import Navigation from '@/components/Navigation'; +import HeaderNav from '@/components/HeaderNav'; +import FooterNav from '@/components/FooterNav'; + +type Props = { + children: ReactNode; +}; + +function CalendarLayout({ children }: Props) { + const theme = useMantineTheme(); + const [opened, setOpened] = useState(false); + const [themeOpened, { open: themeOpen, close: themeClose }] = + useDisclosure(false); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); + + return ( + + + + setOpened((o) => !o)} + desktopOpened={desktopOpened} + mobileOpened={mobileOpened} + toggleDesktop={toggleDesktop} + toggleMobile={toggleMobile} + /> + + + + setOpened(false)} /> + + + {children} + + + + + + + + ); +} + +export default CalendarLayout; diff --git a/app/tasks/page.tsx b/app/tasks/page.tsx new file mode 100644 index 0000000..e6fee56 --- /dev/null +++ b/app/tasks/page.tsx @@ -0,0 +1,30 @@ +import { Anchor, Container, Stack } from '@mantine/core'; +import { KanbanBoard, PageHeader } from '@/components'; +import { PATH_DASHBOARD } from '@/routes'; + +const items = [ + { title: 'Dashboard', href: PATH_DASHBOARD.default }, + { title: 'Tasks', href: '#' }, +].map((item, index) => ( + + {item.title} + +)); + +function Tasks() { + return ( + <> + <> + Tasks | DesignSparx + + + + + + + + + ); +} + +export default Tasks; diff --git a/build-storybook.log b/build-storybook.log new file mode 100644 index 0000000..bf74cbb --- /dev/null +++ b/build-storybook.log @@ -0,0 +1,607 @@ +@storybook/cli v7.5.3 + +info => Cleaning outputDir: C:\Users\Kelvin\AppData\Local\Temp\chromatic--14152-nhk6vEBmjTCq +info => Loading presets +info => Building manager.. +info => Manager built (2.81 s) +info => Building preview.. +info => Copying static files: D:\PhpstormProjects\design-sparx\mantine-analytics-dashboard\node_modules\@storybook\manager\static at C:\Users\Kelvin\AppData\Local\Temp\chromatic--14152-nhk6vEBmjTCq\sb-common-assets +info Addon-docs: using MDX2 +info => Using implicit CSS loaders +info => [@storybook/addon-styling] Applying custom Storybook webpack configuration styling. +info => Using default Webpack5 setup + [webpack.Progress] 0% + + [webpack.Progress] 1% setup before run + [webpack.Progress] 1% setup before run NodeEnvironmentPlugin + [webpack.Progress] 1% setup before run + [webpack.Progress] 2% setup run + [webpack.Progress] 2% setup run + [webpack.Progress] 4% setup normal module factory + [webpack.Progress] 4% setup normal module factory CaseSensitivePathsPlugin + [webpack.Progress] 4% setup normal module factory IgnorePlugin + [webpack.Progress] 4% setup normal module factory + [webpack.Progress] 5% setup context module factory + [webpack.Progress] 5% setup context module factory IgnorePlugin + [webpack.Progress] 5% setup context module factory + [webpack.Progress] 6% setup before compile + [webpack.Progress] 6% setup before compile ProgressPlugin + [webpack.Progress] 6% setup before compile + [webpack.Progress] 7% setup compile + [webpack.Progress] 7% setup compile ExternalsPlugin + [webpack.Progress] 7% setup compile ExternalsPlugin + [webpack.Progress] 7% setup compile + [webpack.Progress] 8% setup compilation + [webpack.Progress] 8% setup compilation unplugin-csf + [webpack.Progress] 8% setup compilation ArrayPushCallbackChunkFormatPlugin + [webpack.Progress] 8% setup compilation JsonpChunkLoadingPlugin + [webpack.Progress] 8% setup compilation StartupChunkDependenciesPlugin + [webpack.Progress] 8% setup compilation ImportScriptsChunkLoadingPlugin + [webpack.Progress] 8% setup compilation FetchCompileWasmPlugin + [webpack.Progress] 8% setup compilation FetchCompileAsyncWasmPlugin + [webpack.Progress] 8% setup compilation WorkerPlugin + [webpack.Progress] 8% setup compilation SplitChunksPlugin + [webpack.Progress] 8% setup compilation RuntimeChunkPlugin + [webpack.Progress] 8% setup compilation ResolverCachePlugin + [webpack.Progress] 8% setup compilation HtmlWebpackPlugin + [webpack.Progress] 8% setup compilation + [webpack.Progress] 9% setup compilation + [webpack.Progress] 9% setup compilation DefinePlugin + [webpack.Progress] 9% setup compilation ProvidePlugin + [webpack.Progress] 9% setup compilation ProgressPlugin + [webpack.Progress] 9% setup compilation DocGenPlugin + [webpack.Progress] 9% setup compilation DefinePlugin + [webpack.Progress] 9% setup compilation DefinePlugin + [webpack.Progress] 9% setup compilation ProvidePlugin + [webpack.Progress] 9% setup compilation ChunkPrefetchPreloadPlugin + [webpack.Progress] 9% setup compilation SourceMapDevToolPlugin + [webpack.Progress] 9% setup compilation JavascriptModulesPlugin + [webpack.Progress] 9% setup compilation JsonModulesPlugin + [webpack.Progress] 9% setup compilation AssetModulesPlugin + [webpack.Progress] 9% setup compilation EntryPlugin + [webpack.Progress] 9% setup compilation RuntimePlugin + [webpack.Progress] 9% setup compilation InferAsyncModulesPlugin + [webpack.Progress] 9% setup compilation DataUriPlugin + [webpack.Progress] 9% setup compilation FileUriPlugin + [webpack.Progress] 9% setup compilation CompatibilityPlugin + [webpack.Progress] 9% setup compilation HarmonyModulesPlugin + [webpack.Progress] 9% setup compilation AMDPlugin + [webpack.Progress] 9% setup compilation RequireJsStuffPlugin + [webpack.Progress] 9% setup compilation CommonJsPlugin + [webpack.Progress] 9% setup compilation LoaderPlugin + [webpack.Progress] 9% setup compilation LoaderPlugin + [webpack.Progress] 9% setup compilation NodeStuffPlugin + [webpack.Progress] 9% setup compilation APIPlugin + [webpack.Progress] 9% setup compilation ExportsInfoApiPlugin + [webpack.Progress] 9% setup compilation WebpackIsIncludedPlugin + [webpack.Progress] 9% setup compilation ConstPlugin + [webpack.Progress] 9% setup compilation UseStrictPlugin + [webpack.Progress] 9% setup compilation RequireIncludePlugin + [webpack.Progress] 9% setup compilation RequireEnsurePlugin + [webpack.Progress] 9% setup compilation RequireContextPlugin + [webpack.Progress] 9% setup compilation ImportPlugin + [webpack.Progress] 9% setup compilation ImportMetaContextPlugin + [webpack.Progress] 9% setup compilation SystemPlugin + [webpack.Progress] 9% setup compilation ImportMetaPlugin + [webpack.Progress] 9% setup compilation URLPlugin + [webpack.Progress] 9% setup compilation DefaultStatsFactoryPlugin + [webpack.Progress] 9% setup compilation DefaultStatsPresetPlugin + [webpack.Progress] 9% setup compilation DefaultStatsPrinterPlugin + [webpack.Progress] 9% setup compilation JavascriptMetaInfoPlugin + [webpack.Progress] 9% setup compilation EnsureChunkConditionsPlugin + [webpack.Progress] 9% setup compilation RemoveEmptyChunksPlugin + [webpack.Progress] 9% setup compilation MergeDuplicateChunksPlugin + [webpack.Progress] 9% setup compilation FlagIncludedChunksPlugin + [webpack.Progress] 9% setup compilation SideEffectsFlagPlugin + [webpack.Progress] 9% setup compilation FlagDependencyExportsPlugin + [webpack.Progress] 9% setup compilation FlagDependencyUsagePlugin + [webpack.Progress] 9% setup compilation InnerGraphPlugin + [webpack.Progress] 9% setup compilation MangleExportsPlugin + [webpack.Progress] 9% setup compilation ModuleConcatenationPlugin + [webpack.Progress] 9% setup compilation NoEmitOnErrorsPlugin + [webpack.Progress] 9% setup compilation RealContentHashPlugin + [webpack.Progress] 9% setup compilation WasmFinalizeExportsPlugin + [webpack.Progress] 9% setup compilation NamedModuleIdsPlugin + [webpack.Progress] 9% setup compilation DeterministicChunkIdsPlugin + [webpack.Progress] 9% setup compilation DefinePlugin + [webpack.Progress] 9% setup compilation TerserPlugin + [webpack.Progress] 9% setup compilation TemplatedPathPlugin + [webpack.Progress] 9% setup compilation RecordIdsPlugin + [webpack.Progress] 9% setup compilation WarnCaseSensitiveModulesPlugin + [webpack.Progress] 9% setup compilation IgnoreWarningsPlugin + [webpack.Progress] 9% setup compilation + [webpack.Progress] 10% building + [webpack.Progress] 10% building 0/1 entries 0/0 dependencies 0/0 modules +WARN No story files found for the specified pattern: components\**\*.stories.mdx + [webpack.Progress] 10% building 0/1 entries 1/1 dependencies 0/0 modules + [webpack.Progress] 26% building 0/1 entries 11/20 dependencies 3/10 modules + [webpack.Progress] 18% building import loader ./node_modules/babel-loader/lib/index.js + [webpack.Progress] 18% building 0/1 entries 20/28 dependencies 3/20 modules + [webpack.Progress] 20% building 0/1 entries 25/104 dependencies 4/21 modules + [webpack.Progress] 20% building 0/1 entries 31/110 dependencies 5/27 modules + [webpack.Progress] 21% building 0/1 entries 31/110 dependencies 6/28 modules + [webpack.Progress] 21% building import loader ./node_modules/@storybook/mdx2-csf/loader.js + [webpack.Progress] 21% building 0/1 entries 31/110 dependencies 6/28 modules + [webpack.Progress] 16% building import loader ./node_modules/unplugin/dist/webpack/loaders/load.js + [webpack.Progress] 16% building 0/1 entries 103/156 dependencies 8/63 modules + [webpack.Progress] 15% building import loader ./node_modules/@storybook/nextjs/dist/next-image-loader-stub.js + [webpack.Progress] 15% building 0/1 entries 121/161 dependencies 8/79 modules + [webpack.Progress] 16% building 0/1 entries 159/192 dependencies 11/96 modules + [webpack.Progress] 24% building 0/1 entries 240/594 dependencies 38/148 modules + [webpack.Progress] 15% building import loader ./node_modules/style-loader/dist/cjs.js + [webpack.Progress] 15% building import loader ./node_modules/css-loader/dist/cjs.js + [webpack.Progress] 15% building import loader ./node_modules/postcss-loader/dist/cjs.js + [webpack.Progress] 15% building 0/1 entries 524/595 dependencies 39/398 modules + [webpack.Progress] 14% building 0/1 entries 583/642 dependencies 41/454 modules + [webpack.Progress] 15% building 0/1 entries 659/686 dependencies 46/480 modules + [webpack.Progress] 16% building 0/1 entries 674/700 dependencies 55/490 modules + [webpack.Progress] 19% building 0/1 entries 783/900 dependencies 86/511 modules + [webpack.Progress] 22% building 0/1 entries 985/1100 dependencies 120/550 modules + [webpack.Progress] 21% building 0/1 entries 1300/1362 dependencies 126/627 modules + [webpack.Progress] 21% building 0/1 entries 1700/1906 dependencies 134/660 modules + [webpack.Progress] 21% building 0/1 entries 1890/2000 dependencies 137/684 modules + [webpack.Progress] 21% building 0/1 entries 1995/2500 dependencies 138/689 modules + [webpack.Progress] 20% building 0/1 entries 2100/2532 dependencies 138/694 modules + [webpack.Progress] 20% building 0/1 entries 2600/7430 dependencies 141/766 modules + [webpack.Progress] 17% building 0/1 entries 2900/7430 dependencies 141/1066 modules + [webpack.Progress] 15% building 0/1 entries 3200/7430 dependencies 141/1366 modules + [webpack.Progress] 14% building 0/1 entries 3700/7430 dependencies 141/1866 modules + [webpack.Progress] 12% building 0/1 entries 4433/7430 dependencies 141/2600 modules + [webpack.Progress] 12% building 0/1 entries 5000/7430 dependencies 141/3166 modules + [webpack.Progress] 12% building 0/1 entries 5400/7430 dependencies 141/3566 modules + [webpack.Progress] 11% building 0/1 entries 6000/7432 dependencies 141/4166 modules + [webpack.Progress] 11% building 0/1 entries 6300/7432 dependencies 141/4466 modules + [webpack.Progress] 11% building 0/1 entries 6533/7432 dependencies 141/4700 modules + [webpack.Progress] 11% building 0/1 entries 6833/7442 dependencies 141/5000 modules + [webpack.Progress] 11% building 0/1 entries 7200/7442 dependencies 141/5366 modules + [webpack.Progress] 11% building 0/1 entries 7500/7526 dependencies 143/5594 modules + [webpack.Progress] 11% building 0/1 entries 7600/7764 dependencies 160/5610 modules + [webpack.Progress] 11% building 0/1 entries 7679/7800 dependencies 164/5625 modules + [webpack.Progress] 12% building 0/1 entries 7900/7970 dependencies 225/5732 modules + [webpack.Progress] 13% building 0/1 entries 8162/8200 dependencies 382/5875 modules + [webpack.Progress] 14% building 0/1 entries 8252/8300 dependencies 460/5880 modules + [webpack.Progress] 14% building 0/1 entries 8391/8400 dependencies 462/5910 modules + [webpack.Progress] 14% building 0/1 entries 8572/8600 dependencies 462/5910 modules + [webpack.Progress] 14% building 0/1 entries 8700/8734 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 8800/8836 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 8976/9000 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9087/9100 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9278/9300 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9356/9400 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9500/9534 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9600/9637 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9778/9800 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9878/9900 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 9982/10000 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10038/10100 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10100/10135 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10138/10200 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10238/10300 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10348/10400 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10451/10500 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10551/10600 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10651/10700 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10853/10900 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 10957/11000 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11058/11100 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11162/11200 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11392/11400 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11559/11600 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11678/11700 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11777/11800 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 11979/12000 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12092/12100 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12291/12300 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12473/12500 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12578/12600 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12669/12700 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12778/12800 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 12900/12972 dependencies 462/5917 modules + [webpack.Progress] 14% building 0/1 entries 13075/13100 dependencies 462/5917 modules + [webpack.Progress] 15% building 0/1 entries 13242/13400 dependencies 572/5958 modules + [webpack.Progress] 15% building 0/1 entries 13400/13434 dependencies 629/5992 modules + [webpack.Progress] 16% building 0/1 entries 13461/13492 dependencies 682/6000 modules + [webpack.Progress] 16% building 0/1 entries 13500/13527 dependencies 698/6007 modules + [webpack.Progress] 16% building 0/1 entries 13534/13600 dependencies 719/6014 modules + [webpack.Progress] 16% building 0/1 entries 13600/13687 dependencies 743/6021 modules + [webpack.Progress] 16% building 0/1 entries 13685/13800 dependencies 766/6043 modules + [webpack.Progress] 60% building 0/1 entries 13900/13930 dependencies 5614/6158 modules + [webpack.Progress] 60% building 0/1 entries 14100/14124 dependencies 5679/6229 modules + [webpack.Progress] 60% building 0/1 entries 14336/14400 dependencies 5798/6293 modules + [webpack.Progress] 59% building 0/1 entries 14641/14700 dependencies 5836/6512 modules + [webpack.Progress] 60% building 0/1 entries 14818/14900 dependencies 5976/6551 modules + [webpack.Progress] 60% building 0/1 entries 15099/15132 dependencies 6100/6622 modules + [webpack.Progress] 60% building 0/1 entries 15255/15500 dependencies 6177/6662 modules + [webpack.Progress] 59% building 0/1 entries 15580/15700 dependencies 6252/6897 modules + [webpack.Progress] 60% building 0/1 entries 15816/15930 dependencies 6300/6919 modules + [webpack.Progress] 61% building 0/1 entries 15980/16000 dependencies 6499/6978 modules + [webpack.Progress] 61% building 0/1 entries 16057/16121 dependencies 6600/7004 modules + [webpack.Progress] 62% building 0/1 entries 16362/16400 dependencies 6694/7064 modules + [webpack.Progress] 63% building 0/1 entries 16579/16700 dependencies 6853/7092 modules + [webpack.Progress] 64% building 0/1 entries 16861/16871 dependencies 7100/7183 modules + [webpack.Progress] 64% building 0/1 entries 17002/17002 dependencies 7200/7230 modules + [webpack.Progress] 65% building 1/1 entries 17002/17002 dependencies 7230/7230 modules + [webpack.Progress] 65% building + [webpack.Progress] 69% building finish + [webpack.Progress] 69% building finish + [webpack.Progress] 70% sealing finish module graph + [webpack.Progress] 70% sealing finish module graph ResolverCachePlugin + [webpack.Progress] 70% sealing finish module graph InferAsyncModulesPlugin + [webpack.Progress] 70% sealing finish module graph FlagDependencyExportsPlugin + [webpack.Progress] 70% sealing finish module graph InnerGraphPlugin + [webpack.Progress] 70% sealing finish module graph WasmFinalizeExportsPlugin + [webpack.Progress] 70% sealing finish module graph + [webpack.Progress] 70% sealing plugins + [webpack.Progress] 70% sealing plugins DocGenPlugin + [webpack.Progress] 70% sealing plugins WarnCaseSensitiveModulesPlugin + [webpack.Progress] 70% sealing plugins + [webpack.Progress] 71% sealing dependencies optimization + [webpack.Progress] 71% sealing dependencies optimization SideEffectsFlagPlugin + [webpack.Progress] 71% sealing dependencies optimization FlagDependencyUsagePlugin + [webpack.Progress] 71% sealing dependencies optimization + [webpack.Progress] 71% sealing after dependencies optimization + [webpack.Progress] 71% sealing after dependencies optimization + [webpack.Progress] 72% sealing chunk graph + [webpack.Progress] 72% sealing chunk graph + [webpack.Progress] 73% sealing after chunk graph + [webpack.Progress] 73% sealing after chunk graph + [webpack.Progress] 73% sealing optimizing + [webpack.Progress] 73% sealing optimizing + [webpack.Progress] 74% sealing module optimization + [webpack.Progress] 74% sealing module optimization + [webpack.Progress] 75% sealing after module optimization + [webpack.Progress] 75% sealing after module optimization + [webpack.Progress] 75% sealing chunk optimization + [webpack.Progress] 75% sealing chunk optimization EnsureChunkConditionsPlugin + [webpack.Progress] 75% sealing chunk optimization RemoveEmptyChunksPlugin + [webpack.Progress] 75% sealing chunk optimization MergeDuplicateChunksPlugin + [webpack.Progress] 75% sealing chunk optimization SplitChunksPlugin + [webpack.Progress] 75% sealing chunk optimization RemoveEmptyChunksPlugin + [webpack.Progress] 75% sealing chunk optimization + [webpack.Progress] 76% sealing after chunk optimization + [webpack.Progress] 76% sealing after chunk optimization + [webpack.Progress] 77% sealing module and chunk tree optimization + [webpack.Progress] 77% sealing module and chunk tree optimization PersistentChildCompilerSingletonPlugin + [webpack.Progress] 77% sealing module and chunk tree optimization + [webpack.Progress] 77% sealing after module and chunk tree optimization + [webpack.Progress] 77% sealing after module and chunk tree optimization + [webpack.Progress] 78% sealing chunk modules optimization + [webpack.Progress] 78% sealing chunk modules optimization ModuleConcatenationPlugin + [webpack.Progress] 78% sealing chunk modules optimization + [webpack.Progress] 78% sealing after chunk modules optimization + [webpack.Progress] 78% sealing after chunk modules optimization + [webpack.Progress] 79% sealing module reviving + [webpack.Progress] 79% sealing module reviving RecordIdsPlugin + [webpack.Progress] 79% sealing module reviving + [webpack.Progress] 80% sealing before module ids + [webpack.Progress] 80% sealing before module ids + [webpack.Progress] 80% sealing module ids + [webpack.Progress] 80% sealing module ids NamedModuleIdsPlugin + [webpack.Progress] 80% sealing module ids + [webpack.Progress] 81% sealing module id optimization + [webpack.Progress] 81% sealing module id optimization + [webpack.Progress] 82% sealing module id optimization + [webpack.Progress] 82% sealing module id optimization + [webpack.Progress] 82% sealing chunk reviving + [webpack.Progress] 82% sealing chunk reviving RecordIdsPlugin + [webpack.Progress] 82% sealing chunk reviving + [webpack.Progress] 83% sealing before chunk ids + [webpack.Progress] 83% sealing before chunk ids + [webpack.Progress] 84% sealing chunk ids + [webpack.Progress] 84% sealing chunk ids DeterministicChunkIdsPlugin + [webpack.Progress] 84% sealing chunk ids + [webpack.Progress] 84% sealing chunk id optimization + [webpack.Progress] 84% sealing chunk id optimization FlagIncludedChunksPlugin + [webpack.Progress] 84% sealing chunk id optimization + [webpack.Progress] 85% sealing after chunk id optimization + [webpack.Progress] 85% sealing after chunk id optimization + [webpack.Progress] 86% sealing record modules + [webpack.Progress] 86% sealing record modules RecordIdsPlugin + [webpack.Progress] 86% sealing record modules + [webpack.Progress] 86% sealing record chunks + [webpack.Progress] 86% sealing record chunks RecordIdsPlugin + [webpack.Progress] 86% sealing record chunks + [webpack.Progress] 87% sealing module hashing + [webpack.Progress] 87% sealing module hashing + [webpack.Progress] 87% sealing code generation + [webpack.Progress] 87% sealing code generation + [webpack.Progress] 88% sealing runtime requirements + [webpack.Progress] 88% sealing runtime requirements + [webpack.Progress] 89% sealing hashing + [webpack.Progress] 89% sealing hashing + [webpack.Progress] 89% sealing after hashing + [webpack.Progress] 89% sealing after hashing + [webpack.Progress] 90% sealing record hash + [webpack.Progress] 90% sealing record hash + [webpack.Progress] 91% sealing module assets processing + [webpack.Progress] 91% sealing module assets processing + [webpack.Progress] 91% sealing chunk assets processing + [webpack.Progress] 91% sealing chunk assets processing + [webpack.Progress] 92% sealing asset processing + [webpack.Progress] 92% sealing asset processing PersistentChildCompilerSingletonPlugin + [webpack.Progress] 92% sealing asset processing TerserPlugin + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin main.4dc9119c.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin main.4dc9119c.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin runtime~main.74a45627.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin runtime~main.74a45627.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Configure-mdx.74a333d0.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Configure-mdx.74a333d0.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Button-stories.9d0ec601.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Button-stories.9d0ec601.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Header-stories.aa2c2899.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Header-stories.aa2c2899.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Page-stories.611bec88.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Page-stories.611bec88.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin AddTaskCard-AddTaskCard-stories.06c9dea1.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin AddTaskCard-AddTaskCard-stories.06c9dea1.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ChatItem-ChatItem-stories.e71989f0.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ChatItem-ChatItem-stories.e71989f0.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ChatsList-ChatList-stories.327c48b7.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ChatsList-ChatList-stories.327c48b7.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ErrorAlert-ErrorAlert-stories.0de428f8.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ErrorAlert-ErrorAlert-stories.0de428f8.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Faqs-Faqs-stories.4ce151fc.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Faqs-Faqs-stories.4ce151fc.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin FilterDateMenu-FilterDateMenu-stories.b2cd69fb.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin FilterDateMenu-FilterDateMenu-stories.b2cd69fb.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin InvoiceDetailsCard-InvoiceDetailsCard-stories.64d1f476.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin InvoiceDetailsCard-InvoiceDetailsCard-stories.64d1f476.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin KanbanCard-KanbanCard-stories.805d9eee.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin KanbanCard-KanbanCard-stories.805d9eee.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin LanguagePicker-LanguagePicker-stories.60137fb6.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin LanguagePicker-LanguagePicker-stories.60137fb6.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin LanguageTable-LanguageTable-stories.66a68c24.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin LanguageTable-LanguageTable-stories.66a68c24.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Logo-Logo-stories.7e270fa1.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Logo-Logo-stories.7e270fa1.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin MapChart-MapChart-stories.e91f27c2.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin MapChart-MapChart-stories.e91f27c2.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin MobileDesktopChart-MobileDesktopChart-stories.fb67e5f1.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin MobileDesktopChart-MobileDesktopChart-stories.fb67e5f1.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin OrdersTable-OrdersTable-stories.c6f4d319.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin OrdersTable-OrdersTable-stories.c6f4d319.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin PageHeader-PageHeader-stories.f2b2d8cd.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin PageHeader-PageHeader-stories.f2b2d8cd.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin PricingCard-PricingCard-stories.dfa5a16e.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin PricingCard-PricingCard-stories.dfa5a16e.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ProfileStatsCard-ProfileStatsCard-stories.194b8ec5.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ProfileStatsCard-ProfileStatsCard-stories.194b8ec5.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ProjectsCard-ProjectsCard-stories.c99c09b6.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ProjectsCard-ProjectsCard-stories.c99c09b6.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ProjectsTable-ProjectsTable-stories.733d0610.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin ProjectsTable-ProjectsTable-stories.733d0610.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin RevenueChart-RevenueChart-stories.51a975d7.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin RevenueChart-RevenueChart-stories.51a975d7.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin SalesChart-SalesChart-stories.0a42e94d.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin SalesChart-SalesChart-stories.0a42e94d.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin StatsCard-StatsCard-stories.36e8b0e8.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin StatsCard-StatsCard-stories.36e8b0e8.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin StatsGrid-StatsGrid-stories.2d25e168.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin StatsGrid-StatsGrid-stories.2d25e168.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Surface-Surface-stories.2785aa7f.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin Surface-Surface-stories.2785aa7f.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin TextEditor-TextEditor-stories.844d3ce1.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin TextEditor-TextEditor-stories.844d3ce1.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin TrafficTable-TrafficTable-stories.8a242c7b.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin TrafficTable-TrafficTable-stories.8a242c7b.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin UserButton-UserButton-stories.a4dc1bc1.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin UserButton-UserButton-stories.a4dc1bc1.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin UserProfileCard-UserProfile-stories.63589a10.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin UserProfileCard-UserProfile-stories.63589a10.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5271.50d5e4d8.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5271.50d5e4d8.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1729.c8f45a5c.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1729.c8f45a5c.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 9296.0d7f404d.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 9296.0d7f404d.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 3426.669f67bc.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 3426.669f67bc.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1984.0e5b84af.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1984.0e5b84af.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 7229.5c00e1c8.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 7229.5c00e1c8.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 9433.5b70100d.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 9433.5b70100d.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5997.e8e5517a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5997.e8e5517a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1068.8db51ecc.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1068.8db51ecc.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 4333.14687d4c.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 4333.14687d4c.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 893.b6f7083e.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 893.b6f7083e.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 610.4baa3688.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 610.4baa3688.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 8128.b599836e.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 8128.b599836e.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 9595.bba2eaa3.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 9595.bba2eaa3.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1664.096f6f14.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 1664.096f6f14.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 8098.7374c601.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 8098.7374c601.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 8137.23d0fa61.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 8137.23d0fa61.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5349.83de1a80.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5349.83de1a80.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 4210.756f2704.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 4210.756f2704.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 6120.c346b33a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 6120.c346b33a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 7562.406387a7.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 7562.406387a7.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin resolve sources + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 3426.669f67bc.iframe.bundle.js attach SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 3426.669f67bc.iframe.bundle.js attached SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5349.83de1a80.iframe.bundle.js attach SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 5349.83de1a80.iframe.bundle.js attached SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 4210.756f2704.iframe.bundle.js attach SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 4210.756f2704.iframe.bundle.js attached SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 6120.c346b33a.iframe.bundle.js attach SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin 6120.c346b33a.iframe.bundle.js attached SourceMap + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin resolve sources + [webpack.Progress] 92% sealing asset processing SourceMapDevToolPlugin + [webpack.Progress] 92% sealing asset processing HtmlWebpackPlugin + [webpack.Progress] 92% sealing asset processing HtmlWebpackPlugin + [webpack.Progress] 92% sealing asset processing HtmlWebpackPlugin resolve sources + [webpack.Progress] 92% sealing asset processing HtmlWebpackPlugin + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin main.c7b28d92.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin main.c7b28d92.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin runtime~main.0c4dbfe6.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin runtime~main.0c4dbfe6.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Configure-mdx.b43b4855.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Configure-mdx.b43b4855.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Button-stories.b8791e09.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Button-stories.b8791e09.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Header-stories.ec4b0663.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Header-stories.ec4b0663.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Page-stories.d145554b.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Page-stories.d145554b.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin AddTaskCard-AddTaskCard-stories.0ee2b903.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin AddTaskCard-AddTaskCard-stories.0ee2b903.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ChatItem-ChatItem-stories.a9fb16af.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ChatItem-ChatItem-stories.a9fb16af.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ChatsList-ChatList-stories.5362a490.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ChatsList-ChatList-stories.5362a490.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ErrorAlert-ErrorAlert-stories.5ba8a6b7.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ErrorAlert-ErrorAlert-stories.5ba8a6b7.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Faqs-Faqs-stories.9a02d5e2.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Faqs-Faqs-stories.9a02d5e2.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin FilterDateMenu-FilterDateMenu-stories.098b0a3f.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin FilterDateMenu-FilterDateMenu-stories.098b0a3f.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin InvoiceDetailsCard-InvoiceDetailsCard-stories.456e1d3f.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin InvoiceDetailsCard-InvoiceDetailsCard-stories.456e1d3f.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin KanbanCard-KanbanCard-stories.612af0ac.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin KanbanCard-KanbanCard-stories.612af0ac.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin LanguagePicker-LanguagePicker-stories.03c46a7d.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin LanguagePicker-LanguagePicker-stories.03c46a7d.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin LanguageTable-LanguageTable-stories.e08ba0d0.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin LanguageTable-LanguageTable-stories.e08ba0d0.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Logo-Logo-stories.b4614656.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Logo-Logo-stories.b4614656.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin MapChart-MapChart-stories.15f90a8d.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin MapChart-MapChart-stories.15f90a8d.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin MobileDesktopChart-MobileDesktopChart-stories.50d8f56c.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin MobileDesktopChart-MobileDesktopChart-stories.50d8f56c.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin OrdersTable-OrdersTable-stories.17ea66ff.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin OrdersTable-OrdersTable-stories.17ea66ff.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin PageHeader-PageHeader-stories.ed85aae8.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin PageHeader-PageHeader-stories.ed85aae8.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin PricingCard-PricingCard-stories.1f9ae4c8.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin PricingCard-PricingCard-stories.1f9ae4c8.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ProfileStatsCard-ProfileStatsCard-stories.7a36cd34.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ProfileStatsCard-ProfileStatsCard-stories.7a36cd34.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ProjectsCard-ProjectsCard-stories.51d88b72.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ProjectsCard-ProjectsCard-stories.51d88b72.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ProjectsTable-ProjectsTable-stories.ab41add4.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin ProjectsTable-ProjectsTable-stories.ab41add4.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin RevenueChart-RevenueChart-stories.7f0214da.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin RevenueChart-RevenueChart-stories.7f0214da.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin SalesChart-SalesChart-stories.791f0e48.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin SalesChart-SalesChart-stories.791f0e48.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin StatsCard-StatsCard-stories.1ff541f4.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin StatsCard-StatsCard-stories.1ff541f4.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin StatsGrid-StatsGrid-stories.7aabaa2a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin StatsGrid-StatsGrid-stories.7aabaa2a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Surface-Surface-stories.d8ea583a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin Surface-Surface-stories.d8ea583a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin TextEditor-TextEditor-stories.4bd01e6b.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin TextEditor-TextEditor-stories.4bd01e6b.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin TrafficTable-TrafficTable-stories.c9e1780a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin TrafficTable-TrafficTable-stories.c9e1780a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin UserButton-UserButton-stories.735a0186.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin UserButton-UserButton-stories.735a0186.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin UserProfileCard-UserProfile-stories.aed1e25a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin UserProfileCard-UserProfile-stories.aed1e25a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 5271.34b11095.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 5271.34b11095.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1729.5baf8d59.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1729.5baf8d59.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 9296.73f17864.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 9296.73f17864.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1984.90ea2468.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1984.90ea2468.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 9433.04b3ba97.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 9433.04b3ba97.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 5997.a9604b1d.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 5997.a9604b1d.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1068.4e1d5793.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1068.4e1d5793.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 4333.4bc24251.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 4333.4bc24251.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 893.e3a4524a.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 893.e3a4524a.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 610.f4b76ef1.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 610.f4b76ef1.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 8128.8d4114d9.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 8128.8d4114d9.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 9595.9c99d868.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 9595.9c99d868.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1664.994fdd02.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 1664.994fdd02.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 8098.31939188.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 8098.31939188.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 7562.7e3846bb.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 7562.7e3846bb.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 7229.f63be3e4.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 7229.f63be3e4.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 8137.1fc72667.iframe.bundle.js generate SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin 8137.1fc72667.iframe.bundle.js generated SourceMap + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin resolve sources + [webpack.Progress] 92% sealing asset processing RealContentHashPlugin + [webpack.Progress] 92% sealing asset processing + [webpack.Progress] 93% sealing after asset optimization + [webpack.Progress] 93% sealing after asset optimization + [webpack.Progress] 93% sealing recording + [webpack.Progress] 93% sealing recording + [webpack.Progress] 94% sealing after seal + [webpack.Progress] 94% sealing after seal + [webpack.Progress] 95% emitting emit + [webpack.Progress] 95% emitting emit + [webpack.Progress] 98% emitting after emit + [webpack.Progress] 98% emitting after emit SizeLimitsPlugin + [webpack.Progress] 98% emitting after emit + [webpack.Progress] 99% done plugins + [webpack.Progress] 99% done plugins CaseSensitivePathsPlugin + [webpack.Progress] 99% done plugins + [webpack.Progress] 99% + + [webpack.Progress] 99% cache store build dependencies + [webpack.Progress] 99% cache store build dependencies + [webpack.Progress] 99% cache begin idle + [webpack.Progress] 99% cache begin idle + [webpack.Progress] 100% + +WARN Critical dependency: require function is used in a way in which dependencies cannot be statically extracted +WARN Critical dependency: require function is used in a way in which dependencies cannot be statically extracted +WARN asset size limit: The following asset(s) exceed the recommended size limit (244 KiB). +WARN This can impact web performance. +WARN Assets: +WARN static/media/thumbnail-img.33a9fb29.png (7.1 MiB) +WARN 8128.8d4114d9.iframe.bundle.js (522 KiB) +WARN 7562.7e3846bb.iframe.bundle.js (307 KiB) +WARN 3426.8aec2747.iframe.bundle.js (583 KiB) +WARN 7229.f63be3e4.iframe.bundle.js (509 KiB) +WARN 8137.1fc72667.iframe.bundle.js (837 KiB) +WARN 5349.b73d7adc.iframe.bundle.js (632 KiB) +WARN 4210.25672915.iframe.bundle.js (1.67 MiB) +WARN 6120.25f7a56d.iframe.bundle.js (466 KiB) +WARN entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance. +WARN Entrypoints: +WARN main (1.7 MiB) +WARN runtime~main.0c4dbfe6.iframe.bundle.js +WARN 4210.25672915.iframe.bundle.js +WARN main.c7b28d92.iframe.bundle.js +WARN + [webpack.Progress] 99% cache shutdown + [webpack.Progress] 99% cache shutdown + [webpack.Progress] 100% + +info => Preview built (3.22 min) +info => Output directory: C:\Users\Kelvin\AppData\Local\Temp\chromatic--14152-nhk6vEBmjTCq diff --git a/clerkMiddleware.ts b/clerkMiddleware.ts new file mode 100644 index 0000000..e8141ba --- /dev/null +++ b/clerkMiddleware.ts @@ -0,0 +1,10 @@ +import { authMiddleware } from '@clerk/nextjs'; + +// This example protects all routes including api/trpc routes +// Please edit this to allow other routes to be public as needed. +// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware +export default authMiddleware({}); + +export const config = { + matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'], +}; diff --git a/components/AddTaskCard/AddTaskCard.stories.tsx b/components/AddTaskCard/AddTaskCard.stories.tsx new file mode 100644 index 0000000..d44bfcd --- /dev/null +++ b/components/AddTaskCard/AddTaskCard.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import AddTaskCard from './AddTaskCard'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Tasks/Add Task', + component: AddTaskCard, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + addCard: () => null, + }, +}; diff --git a/components/AddTaskCard/AddTaskCard.tsx b/components/AddTaskCard/AddTaskCard.tsx index 428f00d..10c4e65 100644 --- a/components/AddTaskCard/AddTaskCard.tsx +++ b/components/AddTaskCard/AddTaskCard.tsx @@ -1,33 +1,33 @@ -import React, {useState} from 'react'; -import {Flex, Text, Input, Button} from "@mantine/core"; +'use client'; + +import React, { useState } from 'react'; +import { Flex, Text, Input, Button } from '@mantine/core'; type AddCardProps = { - addCard: (title: string) => void -} + addCard: (title: string) => void; +}; -const AddTaskCard = ({addCard}: AddCardProps) => { - const [title, setTitle] = useState(""); +const AddTaskCard = ({ addCard }: AddCardProps) => { + const [title, setTitle] = useState(''); - return ( - - - Card Title - - setTitle(e.target.value)} - value={title} - /> - - - ); + return ( + + Card Title + setTitle(e.target.value)} + value={title} + /> + + + ); }; export default AddTaskCard; diff --git a/components/AppMain/App.module.css b/components/AppMain/App.module.css new file mode 100644 index 0000000..6e5a89c --- /dev/null +++ b/components/AppMain/App.module.css @@ -0,0 +1,11 @@ +.main { + min-height: 90vh; + + @mixin light { + background-color: var(--mantine-color-gray-1); + } + + @mixin dark { + background-color: var(--mantine-color-dark-6); + } +} diff --git a/components/AppMain/AppMain.tsx b/components/AppMain/AppMain.tsx new file mode 100644 index 0000000..91d528d --- /dev/null +++ b/components/AppMain/AppMain.tsx @@ -0,0 +1,17 @@ +import { ReactNode } from 'react'; +import { Box } from '@mantine/core'; +import classes from './App.module.css'; + +type AppMainProps = { + children: ReactNode; +}; + +const AppMain = ({ children }: AppMainProps) => { + return ( + + {children} + + ); +}; + +export default AppMain; diff --git a/components/AppMain/index.ts b/components/AppMain/index.ts new file mode 100644 index 0000000..89ddc74 --- /dev/null +++ b/components/AppMain/index.ts @@ -0,0 +1 @@ +export { default } from './AppMain'; diff --git a/layout/App/AsideBar/AsideBar.tsx b/components/AsideBar/AsideBar.tsx similarity index 54% rename from layout/App/AsideBar/AsideBar.tsx rename to components/AsideBar/AsideBar.tsx index 6f0c1b7..a33c757 100644 --- a/layout/App/AsideBar/AsideBar.tsx +++ b/components/AsideBar/AsideBar.tsx @@ -1,11 +1,7 @@ import React from 'react'; const AsideBard = () => { - return ( -
- aside bar -
- ); + return
aside bar
; }; export default AsideBard; diff --git a/components/AsideBar/index.ts b/components/AsideBar/index.ts new file mode 100644 index 0000000..c27127a --- /dev/null +++ b/components/AsideBar/index.ts @@ -0,0 +1 @@ +export { default } from './AsideBar'; diff --git a/components/Buttons/ActionButton.stories.tsx b/components/Buttons/ActionButton.stories.tsx new file mode 100644 index 0000000..25cfb51 --- /dev/null +++ b/components/Buttons/ActionButton.stories.tsx @@ -0,0 +1,71 @@ +import type { StoryObj } from '@storybook/react'; + +import ActionButton from './ActionButton'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Buttons/ActionButton', + component: ActionButton, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: [ + 'filled', + 'outline', + 'subtle', + 'transparent', + 'white', + 'light', + 'gradient', + 'default', + ], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + loading: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + variant: 'filled', + }, +}; + +export const GradientVariant: Story = { + args: { + variant: 'gradient', + gradient: { from: 'violet', to: 'indigo', deg: 90 }, + children: 'Gradient button', + }, +}; + +export const CustomIconSize: Story = { + args: { + iconSize: 24, + size: 'xl', + }, +}; diff --git a/components/Buttons/ActionButton.tsx b/components/Buttons/ActionButton.tsx new file mode 100644 index 0000000..a90babf --- /dev/null +++ b/components/Buttons/ActionButton.tsx @@ -0,0 +1,20 @@ +import { ActionIcon, ActionIconProps, rem } from '@mantine/core'; +import { IconUser } from '@tabler/icons-react'; + +type ActionButtonProps = { iconSize?: number } & ActionIconProps; + +/** + * For more docs see - https://mantine.dev/core/action-icon/ + * @param iconSize + * @param others + * @constructor + */ +const ActionButton = ({ iconSize, ...others }: ActionButtonProps) => { + return ( + + + + ); +}; + +export default ActionButton; diff --git a/components/Buttons/ActionButtonGroup.stories.tsx b/components/Buttons/ActionButtonGroup.stories.tsx new file mode 100644 index 0000000..55a8c90 --- /dev/null +++ b/components/Buttons/ActionButtonGroup.stories.tsx @@ -0,0 +1,69 @@ +import type { StoryObj } from '@storybook/react'; + +import ActionButtonGroup from './ActionButtonGroup'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Buttons/ActionButtonsGroup', + component: ActionButtonGroup, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: [ + 'default', + 'filled', + 'outline', + 'subtle', + 'transparent', + 'white', + 'light', + 'gradient', + ], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + loading: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + variant: 'default', + }, +}; + +export const VerticalOrientation: Story = { + args: { + orientation: 'vertical', + }, +}; + +export const CustomIconSize: Story = { + args: { + iconSize: 24, + size: 'xl', + }, +}; diff --git a/components/Buttons/ActionButtonGroup.tsx b/components/Buttons/ActionButtonGroup.tsx new file mode 100644 index 0000000..27209a5 --- /dev/null +++ b/components/Buttons/ActionButtonGroup.tsx @@ -0,0 +1,44 @@ +import { ActionIcon, ActionIconProps, rem } from '@mantine/core'; +import { + IconMessage, + IconNotification, + IconSettingsCog, + IconUser, +} from '@tabler/icons-react'; + +type ActionButtonProps = { + iconSize?: number; + orientation?: 'vertical' | 'horizontal'; +} & ActionIconProps; + +/** + * For more docs see - https://mantine.dev/core/action-icon/#actionicongroup + */ +const ActionButton = ({ + iconSize, + orientation, + ...others +}: ActionButtonProps) => { + return ( + + + + + + + + + + + + + + ); +}; + +export default ActionButton; diff --git a/components/Buttons/Buttons.stories.tsx b/components/Buttons/Buttons.stories.tsx new file mode 100644 index 0000000..51716d3 --- /dev/null +++ b/components/Buttons/Buttons.stories.tsx @@ -0,0 +1,97 @@ +import type { StoryObj } from '@storybook/react'; + +import Buttons from './Buttons'; +import { IconNotebook } from '@tabler/icons-react'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Buttons/Button', + component: Buttons, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: [ + 'filled', + 'outline', + 'subtle', + 'transparent', + 'white', + 'light', + 'gradient', + 'default', + ], + control: { type: 'select' }, + }, + size: { + options: [ + 'xl', + 'lg', + 'md', + 'sm', + 'xs', + 'compact-xl', + 'compact-lg', + 'compact-md', + 'compact-sm', + 'compact-xs', + ], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + loading: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + variant: 'filled', + }, +}; + +export const GradientVariant: Story = { + args: { + variant: 'gradient', + gradient: { from: 'violet', to: 'indigo', deg: 90 }, + children: 'Gradient button', + }, +}; + +export const FullWidth: Story = { + args: { + fullWidth: true, + w: 400, + }, +}; + +export const LeftSection: Story = { + args: { + leftSection: , + children: 'Read more', + }, +}; + +export const RightSection: Story = { + args: { + rightSection: , + children: 'Read more', + }, +}; diff --git a/components/Buttons/Buttons.tsx b/components/Buttons/Buttons.tsx new file mode 100644 index 0000000..ff96c08 --- /dev/null +++ b/components/Buttons/Buttons.tsx @@ -0,0 +1,18 @@ +import { Button, ButtonProps } from '@mantine/core'; + +type ButtonsProps = ButtonProps; + +/** + * For more docs see - https://mantine.dev/core/button/ + * @param others + * @constructor + */ +const Buttons = ({ ...others }: ButtonsProps) => { + return ( + + ); +}; + +export default Buttons; diff --git a/components/Buttons/ButtonsGroup.stories.tsx b/components/Buttons/ButtonsGroup.stories.tsx new file mode 100644 index 0000000..0fa7adc --- /dev/null +++ b/components/Buttons/ButtonsGroup.stories.tsx @@ -0,0 +1,84 @@ +import type { StoryObj } from '@storybook/react'; +import { IconNotebook } from '@tabler/icons-react'; + +import ButtonsGroup from './ButtonsGroup'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Buttons/ButtonGroup', + component: ButtonsGroup, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: [ + 'default', + 'filled', + 'outline', + 'subtle', + 'transparent', + 'white', + 'light', + 'gradient', + ], + control: { type: 'select' }, + }, + size: { + options: [ + 'xl', + 'lg', + 'md', + 'sm', + 'xs', + 'compact-xl', + 'compact-lg', + 'compact-md', + 'compact-sm', + 'compact-xs', + ], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + loading: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + orientation: { + options: ['horizontal', 'vertical'], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + variant: 'default', + }, +}; + +export const VerticalOrientation: Story = { + args: { + orientation: 'vertical', + }, +}; + +export const CustomIconSize: Story = { + args: { + size: 'xl', + }, +}; diff --git a/components/Buttons/ButtonsGroup.tsx b/components/Buttons/ButtonsGroup.tsx new file mode 100644 index 0000000..89a65c2 --- /dev/null +++ b/components/Buttons/ButtonsGroup.tsx @@ -0,0 +1,27 @@ +import { Button, ButtonProps } from '@mantine/core'; + +type ButtonsProps = { orientation?: 'horizontal' | 'vertical' } & ButtonProps; + +/** + * For more docs see - https://mantine.dev/core/button/#buttongroup + * @param orientation + * @param others + * @constructor + */ +const Buttons = ({ orientation, ...others }: ButtonsProps) => { + return ( + + + + + + ); +}; + +export default Buttons; diff --git a/components/CalendarView/CalendarView.stories.tsx b/components/CalendarView/CalendarView.stories.tsx new file mode 100644 index 0000000..842886c --- /dev/null +++ b/components/CalendarView/CalendarView.stories.tsx @@ -0,0 +1,41 @@ +import type { StoryObj } from '@storybook/react'; + +import CalendarView from './CalendarView'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Dates/Calendar', + component: CalendarView, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: ['default', 'filled', 'unstyled'], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: {}, +}; diff --git a/components/CalendarView/CalendarView.tsx b/components/CalendarView/CalendarView.tsx new file mode 100644 index 0000000..c4a3903 --- /dev/null +++ b/components/CalendarView/CalendarView.tsx @@ -0,0 +1,14 @@ +import { Calendar, CalendarProps } from '@mantine/dates'; + +type CalendarViewProps = CalendarProps; + +/** + * For more docs see - https://mantine.dev/dates/calendar/ + * @param others + * @constructor + */ +const CalendarView = ({ ...others }: CalendarViewProps) => { + return ; +}; + +export default CalendarView; diff --git a/components/ChatItem/ChatItem.module.css b/components/ChatItem/ChatItem.module.css new file mode 100644 index 0000000..c2cbed7 --- /dev/null +++ b/components/ChatItem/ChatItem.module.css @@ -0,0 +1,23 @@ +.chatItem { + @mixin light { + background-color: var(--mantine-color-white); + color: --var(--mantine-color-black); + } + + @mixin dark { + background-color: --var(--mantine-color-black); + color: var(--mantine-color-white); + } +} + +.isMeChatItem { + color: --var(--mantine-color-white); + + @mixin light { + background-color: var(--mantine-primary-color-filled); + } + + @mixin dark { + background-color: var(--mantine-primary-color-light); + } +} diff --git a/components/ChatItem/ChatItem.stories.tsx b/components/ChatItem/ChatItem.stories.tsx new file mode 100644 index 0000000..908e68b --- /dev/null +++ b/components/ChatItem/ChatItem.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ChatItem from './ChatItem'; + +const MOCKS = { + id: '50d72d75-93b2-4c38-9d48-53a8292bf4bc', + first_name: 'Ilario', + last_name: 'Baldocci', + avatar: + 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8dXNlcnxlbnwwfHwwfHx8MA%3D%3D&auto=format&fit=crop&w=500&q=60', + sent_time: '7/6/2023', + message: + 'Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.', + sender: false, +}; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Chats/Item', + component: ChatItem, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + loading: false, + fullName: MOCKS.first_name + MOCKS.last_name, + ...MOCKS, + }, +}; + +export const IsSender: Story = { + args: { + loading: false, + fullName: 'you', + ...MOCKS, + }, +}; diff --git a/components/ChatItem/ChatItem.tsx b/components/ChatItem/ChatItem.tsx index e3fffa6..f306028 100644 --- a/components/ChatItem/ChatItem.tsx +++ b/components/ChatItem/ChatItem.tsx @@ -1,39 +1,71 @@ -import {Avatar, Box, BoxProps, Flex, Paper, Text, useMantineTheme} from "@mantine/core"; +import { + Avatar, + Box, + BoxProps, + Flex, + Paper, + Skeleton, + Text, +} from '@mantine/core'; +import classes from './ChatItem.module.css'; type ChatItemProps = { - id: string, - fullName: string, - avatar: string, - sent_time: string, - message: string, - sender: boolean -} & BoxProps + id: string; + fullName: string; + avatar: string; + sent_time: string; + message: string; + sender: boolean; + loading?: boolean; +} & BoxProps; const ChatItem = (props: ChatItemProps) => { - const {id, avatar, message, fullName, sender, sent_time, ...others} = props - const theme = useMantineTheme() - const isMe = fullName.toLowerCase() === 'you' + const { + id, + avatar, + message, + fullName, + sender, + sent_time, + loading, + ...others + } = props; + const isMe = fullName.toLowerCase() === 'you'; - return ( - - - - - - {fullName} - {message} - - {sent_time} - - + return loading ? ( + + + + + ) : ( + + + + + + + {fullName} + + + {message} + + + + {sent_time} + - ); + + + ); }; export default ChatItem; diff --git a/components/ChatsList/ChatList.stories.tsx b/components/ChatsList/ChatList.stories.tsx new file mode 100644 index 0000000..31e9ab0 --- /dev/null +++ b/components/ChatsList/ChatList.stories.tsx @@ -0,0 +1,40 @@ +import type { StoryObj } from '@storybook/react'; + +import ChatsList from './ChatsList'; + +const MOCKS = { + id: '53ba415c-c210-4aa2-9b46-c6e5eb1075dc', + first_name: 'Sher', + last_name: 'Bleackley', + email: 'sbleackley1@rediff.com', + avatar: + 'https://images.unsplash.com/flagged/photo-1573740144655-bbb6e88fb18a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8dXNlcnxlbnwwfHwwfHx8MA%3D%3D&auto=format&fit=crop&w=500&q=60', + sent_time: '1/4/2023', + last_message: + 'In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', +}; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Chats/List', + component: ChatsList, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + lastMessage: MOCKS.last_message, + firstName: MOCKS.first_name, + lastName: MOCKS.last_name, + avatar: MOCKS.avatar, + }, +}; diff --git a/components/ChatsList/ChatsList.module.css b/components/ChatsList/ChatsList.module.css new file mode 100644 index 0000000..ba29ba2 --- /dev/null +++ b/components/ChatsList/ChatsList.module.css @@ -0,0 +1,25 @@ +.item { + padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm); + + @mixin hover { + background-color: var(--mantine-primary-color-light); + transition: all ease 150ms; + } + + @mixin light { + border-bottom: rem(1px) solid var(--mantine-color-gray-3); + } + + @mixin dark { + border-bottom: rem(1px) solid var(--mantine-color-gray-7); + } +} + +.itemRounded { + padding: var(--mantine-spacing-xs); + border-radius: var(--mantine-radius-default); + + @mixin hover { + background-color: var(--mantine-primary-color-light); + } +} diff --git a/components/ChatsList/ChatsList.styles.ts b/components/ChatsList/ChatsList.styles.ts deleted file mode 100644 index 1431cde..0000000 --- a/components/ChatsList/ChatsList.styles.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {createStyles, rem} from "@mantine/core"; - -export default createStyles((theme) => ({ - item: { - padding: `${theme.spacing.xs}`, - borderBottom: `1px solid ${theme.colors.gray[3]}`, - - '&:hover': { - backgroundColor: theme.colors[theme.primaryColor][0] - } - }, - itemRounded: { - padding: `${theme.spacing.xs}`, - borderRadius: theme.radius.md, - - '&:hover': { - backgroundColor: theme.colors[theme.primaryColor][0] - } - } -})) \ No newline at end of file diff --git a/components/ChatsList/ChatsList.tsx b/components/ChatsList/ChatsList.tsx index fe71e6b..781fdc3 100644 --- a/components/ChatsList/ChatsList.tsx +++ b/components/ChatsList/ChatsList.tsx @@ -1,48 +1,58 @@ -import {Avatar, Flex, Group, Indicator, Stack, Text, UnstyledButton, UnstyledButtonProps} from "@mantine/core"; -import useStyles from "./ChatsList.styles"; -import {useMediaQuery} from "@mantine/hooks"; +import { + Avatar, + Flex, + Indicator, + Stack, + Text, + UnstyledButton, + UnstyledButtonProps, +} from '@mantine/core'; +import { useMediaQuery } from '@mantine/hooks'; +import classes from './ChatsList.module.css'; type ChatsListProps = { - avatar: string - firstName: string - lastName: string - lastMessage: string -} & UnstyledButtonProps + avatar: string; + firstName: string; + lastName: string; + lastMessage: string; +} & UnstyledButtonProps; -const ChatsList = ({avatar, lastName, lastMessage, firstName}: ChatsListProps) => { - const {classes} = useStyles(); - const tablet_match = useMediaQuery('(max-width: 768px)'); +const ChatsList = ({ + avatar, + lastName, + lastMessage, + firstName, +}: ChatsListProps) => { + const tablet_match = useMediaQuery('(max-width: 768px)'); - return ( - tablet_match ? - - - - - - {firstName} {lastName} - - : - - - - - - - {firstName} {lastName} - {lastMessage} - - - - ); + return tablet_match ? ( + + + + + + + {firstName} {lastName} + + + + ) : ( + + + + + + + + {firstName} {lastName} + + + {lastMessage} + + + + + ); }; export default ChatsList; diff --git a/components/Dates/DateField.stories.tsx b/components/Dates/DateField.stories.tsx new file mode 100644 index 0000000..5e6453d --- /dev/null +++ b/components/Dates/DateField.stories.tsx @@ -0,0 +1,69 @@ +import type { StoryObj } from '@storybook/react'; + +import DateField from './DateField'; +import dayjs from 'dayjs'; +import { IconCalendar } from '@tabler/icons-react'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Dates/DateField', + component: DateField, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: ['default', 'filled', 'unstyled'], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + w: 200, + }, +}; + +export const MinAndMax: Story = { + args: { + minDate: new Date(), + maxDate: dayjs(new Date()).add(1, 'month').toDate(), + w: 200, + }, +}; + +export const WithIcon: Story = { + args: { + leftSection: , + leftSectionPointerEvents: 'none', + w: 200, + }, +}; + +export const Clearable: Story = { + args: { + clearable: true, + defaultValue: new Date(), + w: 200, + }, +}; diff --git a/components/Dates/DateField.tsx b/components/Dates/DateField.tsx new file mode 100644 index 0000000..df5d29b --- /dev/null +++ b/components/Dates/DateField.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { useState } from 'react'; +import { DateInput, DatePickerInputProps } from '@mantine/dates'; + +type DateFieldProps = DatePickerInputProps; + +/** + * For more docs see - https://mantine.dev/dates/date-input/ + * @param others + * @constructor + */ +const DateField = ({ ...others }: DateFieldProps) => { + const [value, setValue] = useState(null); + return ( + // @ts-ignore + + ); +}; + +export default DateField; diff --git a/components/Dates/DateTimeField.stories.tsx b/components/Dates/DateTimeField.stories.tsx new file mode 100644 index 0000000..539b7e8 --- /dev/null +++ b/components/Dates/DateTimeField.stories.tsx @@ -0,0 +1,80 @@ +import type { StoryObj } from '@storybook/react'; + +import DateTimeInput from './DateTimeField'; +import { IconCalendarTime } from '@tabler/icons-react'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Dates/DateTimeField', + component: DateTimeInput, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: ['default', 'filled', 'unstyled'], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + w: 200, + }, +}; + +export const WithSeconds: Story = { + args: { + withSeconds: true, + w: 200, + }, +}; + +export const ModalPicker: Story = { + args: { + dropdownType: 'modal', + w: 200, + }, +}; + +export const WithIcon: Story = { + args: { + leftSection: , + leftSectionPointerEvents: 'none', + w: 200, + }, +}; + +export const Clearable: Story = { + args: { + clearable: true, + w: 200, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + w: 200, + }, +}; diff --git a/components/Dates/DateTimeField.tsx b/components/Dates/DateTimeField.tsx new file mode 100644 index 0000000..570b6a4 --- /dev/null +++ b/components/Dates/DateTimeField.tsx @@ -0,0 +1,20 @@ +import { DateTimePicker, DateTimePickerProps } from '@mantine/dates'; + +type DateTimeFieldProps = DateTimePickerProps; + +/** + * For more docs see - https://mantine.dev/dates/date-time-picker/ + * @param others + * @constructor + */ +const DateTimeField = ({ ...others }: DateTimeFieldProps) => { + return ( + + ); +}; + +export default DateTimeField; diff --git a/components/Dates/MonthField.stories.tsx b/components/Dates/MonthField.stories.tsx new file mode 100644 index 0000000..818232a --- /dev/null +++ b/components/Dates/MonthField.stories.tsx @@ -0,0 +1,73 @@ +import type { StoryObj } from '@storybook/react'; + +import MonthField from './MonthField'; +import { IconCalendar } from '@tabler/icons-react'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Dates/MonthField', + component: MonthField, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: ['default', 'filled', 'unstyled'], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + w: 200, + }, +}; + +export const ModalPicker: Story = { + args: { + dropdownType: 'modal', + w: 200, + }, +}; + +export const WithIcon: Story = { + args: { + leftSection: , + leftSectionPointerEvents: 'none', + w: 200, + }, +}; + +export const Clearable: Story = { + args: { + clearable: true, + w: 200, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + w: 200, + }, +}; diff --git a/components/Dates/MonthField.tsx b/components/Dates/MonthField.tsx new file mode 100644 index 0000000..d66fbed --- /dev/null +++ b/components/Dates/MonthField.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { MonthPickerInput, MonthPickerInputProps } from '@mantine/dates'; +import { useState } from 'react'; + +type MonthFieldProps = MonthPickerInputProps; + +/** + * For more docs see - https://mantine.dev/dates/month-picker-input/ + * @param others + * @constructor + */ +const MonthField = ({ ...others }: MonthFieldProps) => { + const [value, setValue] = useState(null); + + return ( + + ); +}; + +export default MonthField; diff --git a/components/Dates/TimeField.stories.tsx b/components/Dates/TimeField.stories.tsx new file mode 100644 index 0000000..e99ae0f --- /dev/null +++ b/components/Dates/TimeField.stories.tsx @@ -0,0 +1,66 @@ +import type { StoryObj } from '@storybook/react'; + +import TimeField from './TimeField'; +import { IconCalendar, IconCalendarTime } from '@tabler/icons-react'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Dates/TimeField', + component: TimeField, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: ['default', 'filled', 'unstyled'], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + w: 200, + }, +}; + +export const WithIcon: Story = { + args: { + leftSection: , + leftSectionPointerEvents: 'none', + w: 200, + }, +}; + +export const WithSeconds: Story = { + args: { + withSeconds: true, + w: 200, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + w: 200, + }, +}; diff --git a/components/Dates/TimeField.tsx b/components/Dates/TimeField.tsx new file mode 100644 index 0000000..66dc79a --- /dev/null +++ b/components/Dates/TimeField.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { TimeInputProps } from '@mantine/dates'; + +type TimeFieldProps = TimeInputProps; + +/** + * For more docs see - https://mantine.dev/dates/time-input/ + * @param others + * @constructor + */ +const TimeField = ({ ...others }: TimeFieldProps) => { + return ; +}; + +export default TimeField; diff --git a/components/Dates/YearField.stories.tsx b/components/Dates/YearField.stories.tsx new file mode 100644 index 0000000..d5769e5 --- /dev/null +++ b/components/Dates/YearField.stories.tsx @@ -0,0 +1,73 @@ +import type { StoryObj } from '@storybook/react'; + +import YearField from './YearField'; +import { IconCalendar } from '@tabler/icons-react'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Mantine/Dates/YearField', + component: YearField, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], + argTypes: { + variant: { + options: ['default', 'filled', 'unstyled'], + control: { type: 'select' }, + }, + size: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'select' }, + }, + radius: { + options: ['xl', 'lg', 'md', 'sm', 'xs'], + control: { type: 'inline-radio' }, + }, + disabled: { + options: [true, false], + control: { type: 'inline-radio' }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + w: 200, + }, +}; + +export const ModalPicker: Story = { + args: { + dropdownType: 'modal', + w: 200, + }, +}; + +export const Clearable: Story = { + args: { + clearable: true, + w: 200, + }, +}; + +export const WithIcon: Story = { + args: { + leftSection: , + leftSectionPointerEvents: 'none', + w: 200, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + w: 200, + }, +}; diff --git a/components/Dates/YearField.tsx b/components/Dates/YearField.tsx new file mode 100644 index 0000000..5028dd2 --- /dev/null +++ b/components/Dates/YearField.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { YearPickerInput, YearPickerInputProps } from '@mantine/dates'; +import { useState } from 'react'; + +type YearFieldProps = YearPickerInputProps; + +/** + * For more docs see - https://mantine.dev/dates/year-picker-input/ + * @param others + * @constructor + */ +const YearField = ({ ...others }: YearFieldProps) => { + const [value, setValue] = useState(null); + + return ( + + ); +}; + +export default YearField; diff --git a/components/ErrorAlert/ErrorAlert.stories.tsx b/components/ErrorAlert/ErrorAlert.stories.tsx new file mode 100644 index 0000000..b001a17 --- /dev/null +++ b/components/ErrorAlert/ErrorAlert.stories.tsx @@ -0,0 +1,26 @@ +import type { StoryObj } from '@storybook/react'; + +import ErrorAlert from './ErrorAlert'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Error', + component: ErrorAlert, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + title: 'Error', + message: 'This is an error message', + }, +}; diff --git a/components/ErrorAlert/ErrorAlert.tsx b/components/ErrorAlert/ErrorAlert.tsx new file mode 100644 index 0000000..a3e1ce2 --- /dev/null +++ b/components/ErrorAlert/ErrorAlert.tsx @@ -0,0 +1,18 @@ +import { ReactNode } from 'react'; +import { Alert, AlertProps } from '@mantine/core'; +import { IconBug } from '@tabler/icons-react'; + +type Props = { message: ReactNode } & AlertProps; + +const ErrorAlert = ({ message, ...others }: Props) => { + const icon = ; + const { title } = others; + + return ( + + {message || ''} + + ); +}; + +export default ErrorAlert; diff --git a/components/ErrorAlert/index.ts b/components/ErrorAlert/index.ts new file mode 100644 index 0000000..dbeab04 --- /dev/null +++ b/components/ErrorAlert/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorAlert'; diff --git a/components/Faqs/Faqs.module.css b/components/Faqs/Faqs.module.css new file mode 100644 index 0000000..3a13698 --- /dev/null +++ b/components/Faqs/Faqs.module.css @@ -0,0 +1,42 @@ +.root { + background-color: light-dark( + var(--mantine-color-gray-0), + var(--mantine-color-dark-6) + ); +} + +.item { + background-color: light-dark( + var(--mantine-color-gray-0), + var(--mantine-color-dark-6) + ); + border: rem(1px) solid transparent; + position: relative; + z-index: 0; + transition: transform 150ms ease; + + & [data-active] { + transform: scale(1.03); + z-index: 1; + background-color: var(--mantine-color-violet-6); + color: var(--mantine-color-white); + border-color: light-dark( + var(--mantine-color-gray-2), + var(--mantine-color-dark-4) + ); + box-shadow: var(--mantine-shadow-md); + border-radius: var(--mantine-radius-md); + font-weight: 600; + } +} + +.panel { + padding: var(--mantine-spacing-sm); + font-size: var(--mantine-font-size-sm); +} + +.chevron { + &[data-rotate] { + transform: rotate(-90deg); + } +} diff --git a/components/Faqs/Faqs.stories.tsx b/components/Faqs/Faqs.stories.tsx new file mode 100644 index 0000000..ca02c47 --- /dev/null +++ b/components/Faqs/Faqs.stories.tsx @@ -0,0 +1,25 @@ +import type { StoryObj } from '@storybook/react'; + +import Faqs from './Faqs'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Faqs', + component: Faqs, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + style: { width: 600 }, + }, +}; diff --git a/components/Faqs/Faqs.tsx b/components/Faqs/Faqs.tsx index 2dc5900..3135cb9 100644 --- a/components/Faqs/Faqs.tsx +++ b/components/Faqs/Faqs.tsx @@ -1,68 +1,54 @@ -import {Container, Accordion, useMantineTheme, Title} from '@mantine/core'; +import { Accordion, Box, BoxProps, Title } from '@mantine/core'; +import classes from './Faqs.module.css'; const placeholder = - 'It can’t help but hear a pin drop from over half a mile away, so it lives deep in the mountains where there aren’t many people or PokΓ©mon.It was born from sludge on the ocean floor. In a sterile environment, the germs within its body can’t multiply, and it dies.It has no eyeballs, so it can’t see. It checks its surroundings via the ultrasonic waves it emits from its mouth.'; - -const Faqs = () => { - const theme = useMantineTheme() - - return ( - - - Frequently Asked Questions - - - - - How can I reset my password? - {placeholder} - - - - Can I create more that one account? - {placeholder} - - - - How can I subscribe to monthly newsletter? - {placeholder} - - - - Do you store credit card information securely? - {placeholder} - - - - What payment systems to you work with? - {placeholder} - - - - ); -} - -export default Faqs; \ No newline at end of file + 'It can’t help but hear a pin drop from over half a mile away, so it lives deep in the mountains where there aren’t many people or PokΓ©mon.It was born from sludge on the ocean floor. In a sterile environment, the germs within its body can’t multiply, and it dies.It has no eyeballs, so it can’t see. It checks its surroundings via the ultrasonic waves it emits from its mouth.'; + +type FaqsProps = BoxProps; + +const Faqs = ({ ...others }: FaqsProps) => { + return ( + + + Frequently Asked Questions + + + + + How can I reset my password? + {placeholder} + + + + + Can I create more that one account? + + {placeholder} + + + + + How can I subscribe to monthly newsletter? + + {placeholder} + + + + + Do you store credit card information securely? + + {placeholder} + + + + + What payment systems to you work with? + + {placeholder} + + + + ); +}; + +export default Faqs; diff --git a/components/FilterDateMenu/FilterDateMenu.stories.tsx b/components/FilterDateMenu/FilterDateMenu.stories.tsx new file mode 100644 index 0000000..f44e70b --- /dev/null +++ b/components/FilterDateMenu/FilterDateMenu.stories.tsx @@ -0,0 +1,23 @@ +import type { StoryObj } from '@storybook/react'; + +import FilterDateMenu from './FilterDateMenu'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'DateFilter', + component: FilterDateMenu, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: {}, +}; diff --git a/components/FilterDateMenu/FilterDateMenu.tsx b/components/FilterDateMenu/FilterDateMenu.tsx index f9a95c0..8176764 100644 --- a/components/FilterDateMenu/FilterDateMenu.tsx +++ b/components/FilterDateMenu/FilterDateMenu.tsx @@ -1,23 +1,25 @@ -import {Button, Menu} from "@mantine/core"; -import {IconChevronDown} from "@tabler/icons-react"; +import { Button, Menu } from '@mantine/core'; +import { IconChevronDown } from '@tabler/icons-react'; const FilterDateMenu = () => { - return ( - - - - + return ( + + + + - - Today - Yesterday - Last 7 days - Last 30 days - This month - Last month - - - ); + + Today + Yesterday + Last 7 days + Last 30 days + This month + Last month + + + ); }; -export default FilterDateMenu +export default FilterDateMenu; diff --git a/components/FooterNav/FooterNav.tsx b/components/FooterNav/FooterNav.tsx new file mode 100644 index 0000000..9734a25 --- /dev/null +++ b/components/FooterNav/FooterNav.tsx @@ -0,0 +1,74 @@ +import { + ActionIcon, + Button, + ButtonProps, + Group, + Menu, + rem, + Text, + useMantineColorScheme, + useMantineTheme, +} from '@mantine/core'; +import { useMediaQuery } from '@mantine/hooks'; +import { IconDots } from '@tabler/icons-react'; +import { PATH_GITHUB } from '@/routes'; + +const FooterNav = () => { + const theme = useMantineTheme(); + const { colorScheme } = useMantineColorScheme(); + const mobile_match = useMediaQuery('(max-width: 425px)'); + + const BUTTON_PROPS: ButtonProps = { + variant: 'subtle', + style: { + padding: `${rem(8)} ${rem(12)}`, + color: colorScheme === 'dark' ? theme.white : theme.black, + + '&:hover': { + transition: 'all ease 150ms', + backgroundColor: + colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2], + textDecoration: 'none', + }, + }, + }; + + return ( + + {mobile_match ? ( + + + + + + + + + Support + Help Center + Privacy + Terms of Use + + + ) : ( + + + + + + + )} + + © {new Date().getFullYear()} DesignSparx + + + ); +}; + +export default FooterNav; diff --git a/components/FooterNav/index.ts b/components/FooterNav/index.ts new file mode 100644 index 0000000..8d0dd05 --- /dev/null +++ b/components/FooterNav/index.ts @@ -0,0 +1 @@ +export { default } from './FooterNav'; diff --git a/components/HeaderNav/HeaderNav.tsx b/components/HeaderNav/HeaderNav.tsx new file mode 100644 index 0000000..901ded0 --- /dev/null +++ b/components/HeaderNav/HeaderNav.tsx @@ -0,0 +1,385 @@ +'use client'; + +import { + ActionIcon, + Avatar, + Burger, + Flex, + Group, + Indicator, + MantineTheme, + Menu, + rem, + Stack, + Text, + TextInput, + Tooltip, + useMantineColorScheme, + useMantineTheme, +} from '@mantine/core'; +import { + IconBell, + IconCircleHalf2, + IconLayoutSidebarLeftCollapse, + IconLayoutSidebarLeftExpand, + IconMessageCircle, + IconMoonStars, + IconPower, + IconSearch, + IconSunHigh, +} from '@tabler/icons-react'; +import { LanguagePicker } from '@/components'; +import { upperFirst, useMediaQuery } from '@mantine/hooks'; +import { showNotification } from '@mantine/notifications'; + +const ICON_SIZE = 20; + +const MESSAGES = [ + { + id: '687725a3-5489-486e-9ffd-180df00f8e10', + first_name: 'Egor', + last_name: 'Thornebarrow', + message: + 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', + }, + { + id: '75103d7a-b26b-4e1f-8d7e-2117867d05b8', + first_name: 'Magdalen', + last_name: 'Slessor', + message: + 'In eleifend quam a odio. In hac habitasse platea dictumst. Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + }, + { + id: '08bff541-e731-445f-a2e1-7f08e3e30357', + first_name: 'Bald', + last_name: 'Vant', + message: + 'Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue. Vestibulum rutrum rutrum neque.', + }, +]; + +const NOTIFICATIONS = [ + { + id: '15f6a5e0-758f-4642-aa95-a07bb3170544', + title: 'Beahan-Senger', + message: + 'Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque. Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAK/SURBVDjLbZNNaFRXFMd/72U+dDLNRItGUSeJiagTJ6IQhdhaWoopFCJiF10UBAXpSlHcddHi0oUbkXYRFURE/NiIIjSkpCpdtGoTJG20iUMsMZJokhmqee/de8/p4jmDggcuFw73/s7/nPu/nqrSe/hch6peUZhD6VYUVUCVeNPaEmcwYbn06/nv1gIkiA8cVNhQLOS96ZkyqtVLEMMEFZgvv2IhVEQTrbyJGAA7i4U13qeda8ivLKIxAVGJq0pcfVljhsyiBDt2f8s7AFstyleFDuauXVvjLm516gIAFJVoYqKMl95TRBGvB1vWsBLpBKs29RMe9NSnANVQURxTnEiWFEWAsPlq4PvAyjOCRPTFVJ+kiAIMGGElThvqSORTFFID3Oy+xfqdnUyfLZHvWByX3UGiBOsM4RhyJ5t7bH8WB2qyp27fWxLP2dx8RtyrVuYL61n9Oe+EzUFxgnOWKzzuTD4F6GxWKc4K7Sk/2DPpjINuR3Mjv9Nyov4oGEF2Q/zuRrAWiEyhkhA/TReMgm+sjr1gL0bZ2lc20M4dYlUxmNiaBQTRC+Dhf+6q0PEWIcNLKFxWCcYJ6zkPl93lMi19RJM/oSfsiSzzQSzI4j1P+862v/YrylwggkNoXEExrGkfJuv2sbJtfcSTP6InzRElRaeDtzj+4EGth7tHwLw327BRDGgstyleKXL/LPWN7xJdHzPupSSlhpZur2fX4Y+Yyx+XTtGf2qYSLrsKGl/lk/vflphFVMPTyFEPBqdhWlwYdcW3SYF1H2vUaKDRM5CjpA4aMzPLp0jMd3fiOd30x5ZoqbyYNkMktRxhCRp+8oUFXwfbq2d/JofIZo5Aatmz+mvn49//75D0NNh8g2tWGtoAphENbs6Kkqn+w/3afKAUVZ8eQ4W1uX0bWhhYmonqulTuZMtvYzUa7/fvHI7irgf/y+taODWkwAAAAAAElFTkSuQmCC', + }, + { + id: '28d772cb-dcbd-4a34-bbe2-587fc03c5723', + title: 'Orn, Wehner and Kirlin', + message: 'Integer ac leo.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJzSURBVDjLpZNLSFRxFMZ/944z42MqH2MllWJEkT1UjHETSFJiVAtdtHFly5aCm6xNWyFw2aoIoiAoWgjSIqkWghAuVFBTJJVGGV/pzPznzr3/c9qEaUaLOsuPw4/z+D5HVfmfKvhdCIadmCo9KrSLkFAFFUZFGFLh0YFbmt7d7+yeIBh2WlR44lT31jqlCTRSDmpRs4rdGMP70j8vQndZh37YBwjeOx1a2vLaPdqFFh/CZqcQbw0NcuAU4haewvFdvIVXeCsfOitv65sdgP/eKVfla+jCi5i4Wwy/HCSVNJy5eAhUmR7fIl4Jl6+fJxS9wPeRO2kRaqq6dN0FUKHHremLmewKm/PvCLuGtptFLM1usDS3SdvNIiIRsNsTmPUxis/2xcTSA+ACiHDDKWtGtj8TdbZoSkA0bLjstylekTr9QjRsKEp8XNpb4qC0tOI5cZuQB3Rg6xML+z5yOZymo1ls0dLLRrAw1pqdwAqGA1yePkSVhc9xCr5nJDJKEuzhuScIcgryTlDdjMLqogQ2vGBWObxTUP8ZDVzI5OMf0xREHGJH48SPxJi/VuGmdEtKqostyle07FEFuA7zH7CyAM+qtjDWU1F2koNai/jfo5xObQwHC4CrQ+AFXCFY2Y1a/4eQZ/3cAykJ18mCY4gBs5Aeqg4oHNo9ZHxQdcQrEzaLSe5KeHad9jYAdQ1qkpEbqzs89AjxE62IwTKkdFQRQ3EidceRUtbmVt4i1+nu7Ge5raZ+Xkc+eaWB6X1PXWRirO4YZdkIDAs2SSMyyP9M8HPneb7uvQH7MAsPjUiYmlV4R2a0kEeQh8RgOfocCn/9KDv4TpX+oHaI9cJDajhlcAAAAASUVORK5CYII=', + }, + { + id: '64ee7341-f2a8-42f2-b379-48f523811d49', + title: 'Heathcote-Flatley', + message: + 'Vivamus vel nulla eget eros elementum pellentesque. Quisque porta volutpat erat.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJoSURBVDjLhZJZTxNhFIbnRq/9E/4Rf4JRE+PChV4YYjQxLglRQkgDWhGXRo2VNJWu0IWh0hY6iEvpjl1AWuoImqFCO6V0uqRrzOt8H0mxBvQkTyYnOfOcd775mPn5+WMcx12dm5v74Ha7806ns+JwOOIsyyptNttxAMy/YDwejz6ZTKJQKKDZbKLdbkOSJKTTaVgslrX/CmZnZwu1Wg3VarUjID3BbDZD5d7GE+cWRhwZ3J8SoLD+wMDEOu4ZvqFP9zXMuFyuXLlcphszmQwEQUAwGESpVILBYEC13j6Um9pUg5mZmck2Gg3wPI9isYh4PE4hNT4+DlXkIUZDw3jgH4TC24+Bj324u3CbCq6//gJmenqaClZXV6kgEolQSGk0GhxWRND7MgHGbrdTQSKRwM7ODnw+H/x+Px1Sq9UwenMHQgstyleVVEwVqtVarVaSKVS9PvD4TBisRgVqFSqzkZrINuVIF+qo+dxBMyEXCSyKIr095EDJUmI6OlzNeyhHFgZIiBPNpiFcymLje0yziqDYIxG41GdTtc7pp/CpMWCMa0eJpMJYyYXKpXKoQn4nyWcHvLvXQatVntEaV0Dv7GJCW4Ztk882MAm3i6JFHdUpAKaQk5gl1kTJJwaWty/UYOT31GsNOkwKS6e79roiYko19qdngh6HgX3Bf3mdSrwyC9yf/EukYfzs9gFEZxX+vcFffo0dmXBwvLe5vcr3QlsAbGrpwlG/hDcepNCodyAKNWxVahBEKvySVfAZ0p0+CAuKH2/OoIbmuTitVcr1SsvErj0LIqLoxGcU4ZwZjiAkwrvgZy4w7G/AXhUV4qmXai6AAAAAElFTkSuQmCC', + }, + { + id: '414c4537-5726-466e-9c26-2ab398a17f70', + title: 'Buckridge, Yundt and Schiller', + message: + 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKzSURBVBgZBcFNbJNlAADg53v7tdR1o4vup/yk20IQxqZsLIskCsaTGi7sYDzIzYtctDEkXkwWEhOvix4kRG8eOKkh/iUmDgJiREVh6PidcRnb2Ngcg3Xr1671eaLKm1ozRaNiJQABAQABAFDDrLFk2sk4UzRq90slnUOk0oSYKCJERIEQESKkiBChscnsr6XMt2fFYiWdQ1RSFIZJYeFvHlwhBFIBgRSiQKNBcxsdB+g4W4pBKk3IMjgCGKG6xu0fuPMNm48R0Wgg4r95sk9RJ6gjiinPMfElUxdZWySdY99RXv2I7QcBEdUqG1VqCQmxgBBRnuP654SIONA2wMAx8kWee4crp61NfWWmmpfKD1ibOq+4pVUsIKAwzOESK9PMXmF6nB/fY+g4xcPK2woePXxWoe2QfM+glX/2uPugJlaHFBHSOdp7ae9l3wgXP+D3U8orl1XSZU/uft7y3UmZKNGytSDb1iMWEGH5Kt+9TUc/e0fItfPC+zbG31JJPZTvfkVl7oxMU+TfP2+oV6p2/fSFWEAINALrC0yPM3eZZ44pZ6ls3ym/64iNe6eETE26uUvu0TVtTbtlVx+L1SEi1eDgCdLNTJ83f+5D+gZ19B+1ce8TIV2TrPZYunTVtnKrbC4mIYDQoL2f7cO095qvNax09uvoOyKZ/1Qq01BZ7bJ44Tc7Xz8t2zVErUFCACLWF6mW3bp0xvLyjETR3MTH6jasLe+wNP6z4o4XpbduI/MEm5vUiCVo1LDJ9++6OnnHa8c/c/PMCbcuXLKluF9YmjKwUpNePMeNa9Q2ackRCGaMmf2F5jbynW7fvS/562s9+w/J5fa4/8ekvs6nZfsO0N1DazMtOaYmyBiLKi9rzRSMKiip88blVvu7Ow3v7bE1WdB787qm9YQENSSIkTG2Mevk/++B+Jm41JzeAAAAAElFTkSuQmCC', + }, + { + id: '836cd406-c751-4662-b684-3006bf43e507', + title: 'Glover-Stoltenberg', + message: + 'Suspendisse accumsan tortor quis turpis. Sed ante. Vivamus tortor. Duis mattis egestas metus. Aenean fermentum.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKASURBVDjLjZJfaFJRHMf3sOhlSG8RvfbQS0G9RNBLPRUEPfQQPQQrVkH1VC/BegkEL1O7ouhwORS1beI2///Z9TKk2EMEM3QqTkQKdU5S58VhE9q33zmkrRhjB36ce889n+/5/r73jAAYOaoikYgqGAxKfr9fWFpaGv3/+3HghM/n6y4uLioej0eYm5sbPZZAOBxWBQIBBu/W63V0u10QvOdyuQSHw3HySIEBvLy8vFur1UDPoBagKArsdvsvm80WslqtJw4VCIVCKtosD2AGzs/Pg9pBu93mTghWLBaLYDKZRg+FKazdarUKss9sgxxhvViFGMnC/+UbzGbzvtFoTIqieGoIU1gqAmQKi8PkAG63GySKZrMJ80oeE+8/45VrHd8rNRCs6HS6a4fC5AAUFu+90WggmUziR7OFSU8Kno95BOVP0Gq1fUEQbnABOk32er397e1tkAicTifvncEkjtXVVZTLZWQLJXwIyAzeI/jyMESCpYWFhf1KpYJisQhJkjjMHESjUZRKJbgiVry0PMCTd3dwX329e+v1xTdDAUpZSafT2NnZwebmJgqFAnfAks/lcrD5DHjrfYjwhhlfaxLExDPcEy/gyvMzOi5AYW2tra0hFouh1Wohn89zkWw2i1QqhXHhJgIZAwI5I9jQyY8hyk+ZQI8L0M06PTs7208kEvzXsdQ7nQ4ymQxkWcbtyUuIbthwcATTZibw9w7MzMyMTU9PN1jP7BeyvuPxOCis3tUXZ39qpUfQSOMc1qyM/+tgUHSzxgwGwxbrnwWo0Wh6arX6HG1U39Wdh16a4Cezmb0PMzhYer1+bGpqaotghcGDddosUCnM9p9ZYOu/ASUg4G4xOdG6AAAAAElFTkSuQmCC', + }, + { + id: 'cd5a3cfc-9880-43a1-9baf-045f19e5b410', + title: 'Terry, Abshire and King', + message: + 'Morbi ut odio. Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAK1SURBVDjLbZJtSFNxFIcHgjAQgr6VhBSFIhhJQa842JdeLBMyVIpSSUtFZpIIaqa5UNTpUKebmR82FZmmzpXiHKawhqKUlUVQCEEE1ZfQmFg3n85VLN8uHC738H9+5/mfTQNoNtR4S7iUidHmacYtCl6zwnD1NEMPTPSWhG8+vxnOEzDAZBe8ckm5YaYfnjtgzAI9+QGcuXlbA8ZbghmzDTHRDi965HAzDFYiU6H/LvQWgKsERmuhrxAeXhmi+XLw/4DRZjOTAqsBw9WKgHrR1f6b1JGtxZ6up+2aQo+EOTLBfM60GjBqieJZvcJEp0ysUOgvDaOvKER0jaLrpz3Tjz3NiDU5BFtCGA0XFJxyiyqdQuWJSA0jJjO+NtEzq7p6+gpC6c6b42k5dOVCZ5a8b4M1fo76s6FU6/USAq1XwRht0ojue/ztq3fruaMV3W7cZYiuk5YkHY3xOtF14siQqTHdVB3Xcv8wYgaF4W81oruEX7bszFWbQaIbwHEL0dVNTU3F+Xy+SNHV0RAL5UcClB4MoigCOnIkYN8Pjegu4Xski0lXt6vFmqjqfqPLcErg316v9xdNCWdE9xP3oqDggJbC/atXK967oJHtfuBxPqIbEN1g0Y1Rtyuwg5lh+OjB6XTaDY3JxYbGpJ8ZtRdJNeqWO0uj1QC5gjXZILoZspgdaz+bwDtl8oLb7V4WeLncWjBf1p3Kk1kLM1881I1kkVgXRaohdECz5a8sJXCTwArfX8LXKa5Xnsb1xozrXT3qU+NNp857k6PZuxa3gw8J/EedbLfbfTabzXe+KJrB2VbWPwOvLWrA1ukCewVeFLh1rXcyZ49S7UmjwpOyAlcMp2xvIHCswJ8FLl7fl4PGSzURmDw3Viarb/Vb+jUbAgRuEThuu73I4cpj2bsDqrbUvPqt9v8CPKvGd70s+8YAAAAASUVORK5CYII=', + }, + { + id: 'c958e1be-c7c9-4bb0-bc13-da5eb10db30b', + title: "O'Hara Inc", + message: + 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKOgiihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQIV2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4trRCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC', + }, + { + id: 'd50227d9-8bd9-4b64-8b74-1fe356941149', + title: 'Hand LLC', + message: + 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJsSURBVBgZfcFLbExhGIDh9z9zztw6marQUm1DNZEQEdUpsRC2lLgsJDZNGmFjY2nBErGzENewIEUQRBBBJFW3IC6xqqYSd4tWtOPMnMv/fdqYUIvxPEZVqWbn6sUzm+tyV3OpTMFaQUET2eSN7YdvdFHhUMWudR1OY23NxVwyXbAiWFUiK8YfLa3ZvXFpDxUOVbTUanZKpmZ5Jl9L67w5NDY1IKKICHWZzAkqXKpI6seoftZsae/a7Ez3ikwoaZox63H/7LGACocq9n3fE06dlh3L4hOaFMbLMiH48FTntjWGVLgD1wt+Oj8/Y4zDX8r5rZ/ibYcWvjxV09/R3NJKyXh4JuTDx2HtHVrU28lvZuBap7ateYQxBpRxih9Ydhx4QuvceRQKAU9vncWYNF4yT1NHN6eP3olvv3q/JX7cc8EMXC5I29o+E349jA0T4OTZ2xtRmrqaKZ2zGCyCtfzhKkxDeXHuXvDm7etNLpFBJcSGSVSUn0MP6M6/gvg4PKSq45uupA4+33PJxaiRuMTo0CDRyCckKlO3YD0/2nfxL2VCzoPRk8v4kpxJ86IVSZfIwc3UM2PVfkBAFRuHJL7f5KtfxqqlZBP4McQ2orlhCQaIn70ojXj9sUukqC0TfjsJKL8JKbFkyTEmSYwYPBWsRLgEWCDR0d40Z2XfiEsAKjEYQBUQkAhHIxrcH9QbHzVF1CmiDJPOtPMOOLOSEca5hIrBgiqoBQ1RCUDKqJZQ66O2iMajqPUBYTJXQyH2h5HYA3VAE6h6IClUakAjVEMwITgxtlxmMlfLweehIxsaUBwMhv9RwNwF0WEqfgE9XTQvEQ+I/gAAAABJRU5ErkJggg==', + }, + { + id: 'bc8f3d9b-baed-4bab-9f34-d42f114461d0', + title: 'Waelchi, Weimann and Rutherford', + message: 'Morbi quis tortor id nulla ultrices aliquet.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKKSURBVDjLpZNdSBRRGIbnzOzubstyleBRReBYhTRDziQQlKxbmoKItp0YVRUsBB2UVQsWdkfilHaj6GuZqEkhJaSf6knISqUYIgooogWS2uRwjFd25yZ3Xn7NlKS3bzp4jDMzHne73zPfCMAEP5nzbux6gU5UifwsE+AWSMos89DVczz4xpD8ArjkxUsMW4AwZ7InSWwetJh8Vzo1YzPviNYjfTmQL8rY+KSqI1fFJWYAKrsjjSvgPV4F/DsAGbqFyF0nSVOX2Xu0M3lwKMdCHdlgGDtW5kox23BqGFes2UdBeyD2ZYKgn1Tlcynt6YAPB/TDUkg2PNPB9H1s4pxozWZTlIIgjX9XipVL0CoaW0U9sVINGsF2ahm8l/9OkmWZg3shNWXC/TnwnzgwtdSUR27IDpn942cluSPxZIsRGXpt5eCTINg7Y9pNdy1DejbDjzMhNm+BQSrgXMS/1wi+UdOSQiUOeH32rgwc4PxSH8eMFSECC+A2Z0Ns5PAgXygNxPoTqdrFoz2dMy0bKLTuCk0B6HmjXh3hALINCdZCFYyTFaIKn0mTqa50baZNmZQgAvG/TSMlkjqp5MSHz4h+T8ct+HtYRteFdl5jMTxctFJsjSrLw/hDtfvEL01DQSrBDstylenMToIphPN66H0ZGJL2ckf7ApGejJglazCu+P2XwLBpDp8smG1dS/gonalSDTHjLtm7q1AehyIXA5AS8P2r1xAwhWvtcm0Bjn08Rlg0xrBDvJtHukdBnQuRU6stylexzdDGG9jpiJ3HsvKgEzkpasDEZE3VrMFwszVV6fciuTjWmYLQ8CYN7HNrTQocStwUynUiyWkgWJ9Nzf90Lj115vt/BB3c7vE8KHfNE/gKM7aCNx0eNYwAAAABJRU5ErkJggg==', + }, + { + id: '6e6c861b-aba8-4146-a4c9-26caa1bd8ea4', + title: 'Hettinger-Kiehn', + message: 'Aenean sit amet justo.', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAF/SURBVDjLpZN/S8JgEMd9i1JURPSDIoIkS1FQI4iQgihStExrhGmydGObyZyyYRaybBRFQb/8vxcgBIF92275ApoHx7jjns/37p49LgCuQdzlXmEXd8RON1L4QPjM9NwbQtkXBE+eEWCe4D9+hC99j+XDO3j3b+FJ3CCcvu5a5wgQLXV6ceUT/3Xv3mWPAJayE5/fboAA4dw7nNjspmoDQqevlDAMA+12G61WC1/fP1BVFfV6HbIsUyyKIgRBAMdxVD8drf0BzIU5scl12QZY27ZM13VSbzQapFir1VCtViFJEsUsy6JQKCCfz1P9xFrFBlhX5cTGVyUb4D96oESz2SR1RVFIsVKpoFwuo1gsUpzNZsEwDDKZDNWPhQUb4D0wHHUwHCjZgKVEmxKaptHc/ZmtL8/zNLMVp1IpJJNJxGIxqh/yn9sAT1x31IHbx6L/FtiF3Sv6s+a2NMxE65jaUMwtX9CixiIiRkM8RoKc2XbRVGZhnrGcJcDAr3FQwC803UMOARws7QAAAABJRU5ErkJggg==', + }, +]; + +type HeaderNavProps = { + opened?: boolean; + handleOpen?: () => void; + mobileOpened?: boolean; + toggleMobile?: () => void; + desktopOpened?: boolean; + toggleDesktop?: () => void; +}; + +const HeaderNav = (props: HeaderNavProps) => { + const { + handleOpen, + opened, + desktopOpened, + toggleDesktop, + toggleMobile, + mobileOpened, + } = props; + const theme = useMantineTheme(); + const { setColorScheme, colorScheme } = useMantineColorScheme(); + const laptop_match = useMediaQuery('(max-width: 992px)'); + const tablet_match = useMediaQuery('(max-width: 768px)'); + const mobile_match = useMediaQuery('(max-width: 425px)'); + + const messages = MESSAGES.map((m) => ( + + + + {Array.from(m.first_name)[0]} + {Array.from(m.last_name)[0]} + + + + {m.first_name} {m.last_name} + + + {m.message} + + + + + )); + + const notifications = NOTIFICATIONS.slice(0, 3).map((n) => ( + + + + + + {n.title} + + + {n.message} + + + + + )); + + const handleColorSwitch = (mode: 'light' | 'dark' | 'auto') => { + setColorScheme(mode); + showNotification({ + title: `${upperFirst(mode)} is on`, + message: `You just switched to ${ + colorScheme === 'dark' ? 'light' : 'dark' + } mode. Hope you like it`, + styles: (theme: MantineTheme) => ({ + root: { + backgroundColor: + colorScheme === 'dark' + ? theme.colors.gray[7] + : theme.colors.gray[2], + borderColor: + colorScheme === 'dark' + ? theme.colors.gray[7] + : theme.colors.gray[2], + + '&::before': { + backgroundColor: + colorScheme === 'dark' + ? theme.colors.gray[2] + : theme.colors.gray[7], + }, + }, + + title: { + color: + colorScheme === 'dark' + ? theme.colors.gray[2] + : theme.colors.gray[7], + }, + description: { + color: + colorScheme === 'dark' + ? theme.colors.gray[2] + : theme.colors.gray[7], + }, + closeButton: { + color: + colorScheme === 'dark' + ? theme.colors.gray[2] + : theme.colors.gray[7], + '&:hover': { + backgroundColor: theme.colors.red[5], + color: theme.white, + }, + }, + }), + }); + }; + + return ( + + + + + {desktopOpened ? ( + + ) : ( + + )} + + + + {/**/} + {!mobile_match && ( + } + ml="md" + style={{ width: tablet_match ? 'auto' : rem(400) }} + /> + )} + + + {mobile_match && ( + + + + )} + + + + + + + + + + + + + + {MESSAGES.length} new notifications + + {messages} + + Show all messages + + + + + + + + + + + + + + + + {NOTIFICATIONS.length} new notifications + + {notifications} + + Show all notifications + + + + + + + + + + + + + {colorScheme === 'auto' ? ( + + ) : colorScheme === 'dark' ? ( + + ) : ( + + )} + + + + + + Select color modes + + } + onClick={() => setColorScheme('light')} + > + Light + + } + onClick={() => setColorScheme('dark')} + > + Dark + + } + onClick={() => setColorScheme('auto')} + > + Use System Colors + + + + + + ); +}; + +export default HeaderNav; diff --git a/components/HeaderNav/index.ts b/components/HeaderNav/index.ts new file mode 100644 index 0000000..2688492 --- /dev/null +++ b/components/HeaderNav/index.ts @@ -0,0 +1 @@ +export { default } from './HeaderNav'; diff --git a/components/InvoiceDetailsCard/InvoiceDetailsCard.stories.tsx b/components/InvoiceDetailsCard/InvoiceDetailsCard.stories.tsx new file mode 100644 index 0000000..890cd08 --- /dev/null +++ b/components/InvoiceDetailsCard/InvoiceDetailsCard.stories.tsx @@ -0,0 +1,44 @@ +import type { StoryObj } from '@storybook/react'; + +import InvoiceDetailsCard from './InvoiceDetailsCard'; + +const MOCKS = { + id: '8677a3e2-dde3-4d04-8edd-9d0bcf178f89', + full_name: 'Dannie MacTrustie', + email: 'atysack2r@washingtonpost.com', + address: '5160 Iowa Point', + country: 'China', + status: 'approved', + amount: 6221.88, + issue_date: '7/12/2022', + description: + 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\n\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', + client_email: 'atysack2r@illinois.edu', + client_address: '13 Loeprich Point', + client_country: 'Russia', + client_name: 'Alayne Tysack', + client_company: 'Raynor and Sons', +}; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Invoices/Card', + component: InvoiceDetailsCard, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + data: MOCKS, + style: { width: 700 }, + }, +}; diff --git a/components/InvoiceDetailsCard/InvoiceDetailsCard.tsx b/components/InvoiceDetailsCard/InvoiceDetailsCard.tsx index 35a1d76..822c783 100644 --- a/components/InvoiceDetailsCard/InvoiceDetailsCard.tsx +++ b/components/InvoiceDetailsCard/InvoiceDetailsCard.tsx @@ -1,166 +1,213 @@ -import React from 'react'; -import {Invoices} from "@/types"; +'use client'; + +import { Invoices } from '@/types'; +import { + Button, + Divider, + Flex, + Menu, + Paper, + PaperProps, + Stack, + Table, + Text, + TextProps, + useMantineTheme, +} from '@mantine/core'; import { - Button, - Divider, - Flex, - Menu, - Paper, - PaperProps, - Stack, - Table, - Text, - TextProps, - useMantineTheme -} from "@mantine/core"; -import {IconCloudDownload, IconMail, IconPrinter, IconSend, IconShare} from "@tabler/icons-react"; -import {useMediaQuery} from "@mantine/hooks"; + IconCloudDownload, + IconMail, + IconPrinter, + IconSend, + IconShare, +} from '@tabler/icons-react'; +import { useColorScheme, useMediaQuery } from '@mantine/hooks'; +import { Surface } from '@/components'; -const ICON_SIZE = 16 +const ICON_SIZE = 16; type InvoiceDetailsProps = { - data?: Invoices -} & PaperProps + data?: Invoices; +} & PaperProps; const elements = [ - {description: 'Front and rear brake cables', unitPrice: 100, amount: 100}, - {description: 'New set of pedal arms', unitPrice: 25, amount: 25}, - {description: 'Labor - 3hrs', unitPrice: 15, amount: 15}, + { description: 'Front and rear brake cables', unitPrice: 100, amount: 100 }, + { description: 'New set of pedal arms', unitPrice: 25, amount: 25 }, + { description: 'Labor - 3hrs', unitPrice: 15, amount: 15 }, ]; const TEXT_PROPS: TextProps = { - fz: "sm" -} + fz: 'sm', +}; -const InvoiceDetails = ({data, ...others}: InvoiceDetailsProps) => { - const theme = useMantineTheme() - const LINK_PROPS: TextProps = { - color: theme.colors[theme.primaryColor][7] - } - const tablet_match = useMediaQuery('(max-width: 768px)'); +const InvoiceDetails = ({ data, ...others }: InvoiceDetailsProps) => { + const theme = useMantineTheme(); + const colorScheme = useColorScheme(); + const LINK_PROPS: TextProps = { + c: + colorScheme === 'dark' + ? theme.colors[theme.primaryColor][4] + : theme.colors[theme.primaryColor][6], + td: 'underline', + }; + const tablet_match = useMediaQuery('(max-width: 768px)'); - const rows = elements.map((element) => ( - - {element.description} - ${element.unitPrice} - ${element.amount} - - )); + const rows = elements.map((element) => ( + + {element.description} + ${element.unitPrice} + ${element.amount} + + )); - return ( - - {data ? - - - - - - - - + return ( + + {data ? ( + + + + + + + + - - }>Send email - }>Forward in chat - - - - Hello {data.full_name}, - This is the invoice for a payment of ${data.amount} you made - to {data.client_company} - - - Payment No - {data.id} - - - Payment Date - {data.issue_date} - - - - - - Client - {data.client_name} - {data.client_address} - {data.client_country} - - {data.client_email} - - - Paid to - {data.client_company} - {data.address} - {data.country} - - {data.email} - - - - - - - - - - - - - - {rows} - - - - - - - - - - - - - - - - - - - - - -
DescriptionUnit PriceAmount
Subtotal$140
Shipping$5
Discount2.5%
Total$137.75
- Extra note: Please send all items at the same time to - the - shipping address. Thanks in advance. -
: -

Invoice not selected

- } - - ); + + }> + Send email + + }> + Forward in chat + + +
+
+ Hello {data.full_name}, + + This is the invoice for a payment of ${data.amount} you made to{' '} + {data.client_company} + + + + + Payment No + + {data.id} + + + + Payment Date + + {data.issue_date} + + + + + + + Client + + {data.client_name} + {data.client_address} + {data.client_country} + + {data.client_email} + + + + + Paid to + + {data.client_company} + {data.address} + {data.country} + + {data.email} + + + + + + + + Description + Unit Price + Amount + + + + {rows} + + + Subtotal + $140 + + + + Shipping + $5 + + + + Discount + 2.5% + + + + Total + $137.75 + + +
+ + Extra note: Please send all items at the same time to the shipping + address. Thanks in advance. + +
+ ) : ( +

Invoice not selected

+ )} + + ); }; export default InvoiceDetails; diff --git a/components/InvoicesTable/InvoicesTable.tsx b/components/InvoicesTable/InvoicesTable.tsx index e2c9ad4..58fadd4 100644 --- a/components/InvoicesTable/InvoicesTable.tsx +++ b/components/InvoicesTable/InvoicesTable.tsx @@ -1,221 +1,271 @@ -import React, {useEffect, useMemo, useState} from 'react'; -import {DataTable, DataTableSortStatus} from "mantine-datatable"; +'use client'; + +import { ReactNode, useEffect, useMemo, useState } from 'react'; +import { + DataTable, + DataTableProps, + DataTableSortStatus, +} from 'mantine-datatable'; import { - ActionIcon, - Avatar, - Badge, - Flex, - Group, - MantineColor, - MultiSelect, - Stack, - Text, - TextInput, - Tooltip, useMantineTheme -} from "@mantine/core" + ActionIcon, + Avatar, + Badge, + Flex, + Group, + HoverCard, + MantineColor, + MultiSelect, + Stack, + Text, + TextInput, + Tooltip, + UnstyledButton, + useMantineTheme, +} from '@mantine/core'; import sortBy from 'lodash/sortBy'; -import {Invoices, InvoiceStatus} from "@/types"; -import {useDebouncedValue} from "@mantine/hooks"; -import {IconCloudDownload, IconEye, IconSearch} from "@tabler/icons-react"; -import {useRouter} from "next/router"; -import {PATH_INVOICES} from "@/routes"; +import { Invoices, InvoiceStatus } from '@/types'; +import { useDebouncedValue } from '@mantine/hooks'; +import { IconCloudDownload, IconEye, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { PATH_INVOICES } from '@/routes'; +import { ErrorAlert } from '@/components'; -const PAGE_SIZES = [10, 15, 20]; +const PAGE_SIZES = [5, 10, 20]; const ICON_SIZE = 18; type StatusBadgeProps = { - status: InvoiceStatus -} - -const StatusBadge = ({status}: StatusBadgeProps) => { - let color: MantineColor = ''; - - switch (status) { - case 'sent': - color = "blue" - break; - case 'suspended': - color = "gray" - break; - case 'cancelled': - color = "red" - break; - case 'approved': - color = "green.8" - break; - case 'pending': - color = "cyan.7" - break; - default: - color = "dark" - } + status: InvoiceStatus; +}; + +const StatusBadge = ({ status }: StatusBadgeProps) => { + let color: MantineColor = ''; - return ( - {status} - ) -} + switch (status) { + case 'sent': + color = 'blue'; + break; + case 'suspended': + color = 'gray'; + break; + case 'cancelled': + color = 'red'; + break; + case 'approved': + color = 'green.8'; + break; + case 'pending': + color = 'cyan.7'; + break; + default: + color = 'dark'; + } + + return ( + + {status} + + ); +}; type InvoicesTableProps = { - data: Invoices[] -} - -const InvoicesTable = ({data}: InvoicesTableProps) => { - const theme = useMantineTheme() - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(PAGE_SIZES[0]); - const [selectedRecords, setSelectedRecords] = useState([]); - const [records, setRecords] = useState(data.slice(0, pageSize)); - const [sortStatus, setSortStatus] = useState({columnAccessor: 'full_name', direction: 'asc'}); - const [query, setQuery] = useState(''); - const [debouncedQuery] = useDebouncedValue(query, 200); - const [selectedStatuses, setSelectedStatuses] = useState([]); - const router = useRouter() - const statuses = useMemo(() => { - const statuses = new Set(data.map((e) => e.status)); - // @ts-ignore - return [...statuses]; - }, [data]); - - useEffect(() => { - setPage(1); - }, [pageSize]); - - useEffect(() => { - const from = (page - 1) * pageSize; - const to = from + pageSize; - const d = sortBy(data, sortStatus.columnAccessor) as Invoices[]; - const dd = d.slice(from, to) as Invoices[]; - let filtered = sortStatus.direction === 'desc' ? dd.reverse() : dd - - if (debouncedQuery || selectedStatuses.length) { - filtered = - data.filter(({full_name, status}) => { - if (debouncedQuery !== '' && !full_name.toLowerCase().includes(debouncedQuery.trim().toLowerCase())) { - return false; - } - - // @ts-ignore - if (selectedStatuses.length && !selectedStatuses.some(s => s === status)) { - return false; - } - return true; - }).slice(from, to); - } - - setRecords(filtered); - }, [sortStatus, data, page, pageSize, debouncedQuery, selectedStatuses]); - - return ( - <> - { - const {full_name, email} = item, - firstName = full_name.split(' ')[0], - lastName = full_name.split(' ')[1] - - return ( - - - {Array.from(firstName)[0]}{Array.from(lastName)[0]} - - - {full_name} - {email} - - ) - }, - sortable: true, - filter: ( - } - value={query} - onChange={(e) => setQuery(e.currentTarget.value)} - /> - ), - filtering: query !== '', - }, - { - accessor: 'status', - render: (item) => , - filter: ( - } - clearable - searchable - /> - ), - filtering: selectedStatuses.length > 0, - }, - { - accessor: 'id', - render: (item) => #{item.id.slice(0, 7)} - }, - { - accessor: 'amount', - sortable: true, - render: (item) => ${item.amount} - }, - { - accessor: 'issue_date' - }, - { - accessor: '', - title: 'Actions', - render: (item) => - - - - - - - - router.push(PATH_INVOICES.invoices.invoice_details(item.id))} - > - - - - - } - ]} - records={records} - selectedRecords={selectedRecords} - onSelectedRecordsChange={setSelectedRecords} - totalRecords={(debouncedQuery || selectedStatuses.length > 0) ? records.length : data.length} - recordsPerPage={pageSize} - page={page} - onPageChange={(p) => setPage(p)} - recordsPerPageOptions={PAGE_SIZES} - onRecordsPerPageChange={setPageSize} - sortStatus={sortStatus} - onSortStatusChange={setSortStatus} - /> - - ); + data: Invoices[]; + error?: ReactNode; + loading?: boolean; +}; + +const InvoicesTable = ({ data, error, loading }: InvoicesTableProps) => { + const theme = useMantineTheme(); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(PAGE_SIZES[0]); + const [selectedRecords, setSelectedRecords] = useState([]); + const [records, setRecords] = useState(data.slice(0, pageSize)); + const [sortStatus, setSortStatus] = useState({ + columnAccessor: 'full_name', + direction: 'asc', + }); + const [query, setQuery] = useState(''); + const [debouncedQuery] = useDebouncedValue(query, 200); + const [selectedStatuses, setSelectedStatuses] = useState([]); + const router = useRouter(); + const statuses = useMemo(() => { + const statuses = new Set(data.map((e) => e.status)); + // @ts-ignore + return [...statuses]; + }, [data]); + + const columns: DataTableProps['columns'] = [ + { + accessor: 'full_name', + title: 'Customer', + render: ({ full_name, email }: any) => { + const firstName = full_name.split(' ')[0], + lastName = full_name.split(' ')[1]; + + return ( + + + + + {`${Array.from(firstName)[0]}`} + {`${Array.from(lastName)[0]}`} + + + {full_name} + {email} + + + + + + First name: {firstName} + Last name: {lastName} + Email: {email} + + + + ); + }, + sortable: true, + filter: ( + } + value={query} + onChange={(e) => setQuery(e.currentTarget.value)} + /> + ), + filtering: query !== '', + }, + { + accessor: 'status', + render: (item: any) => , + filter: ( + } + clearable + searchable + /> + ), + filtering: selectedStatuses.length > 0, + }, + { + accessor: 'id', + render: (item: any) => #{item.id.slice(0, 7)}, + }, + { + accessor: 'amount', + sortable: true, + render: (item: any) => ${item.amount}, + }, + { + accessor: 'issue_date', + }, + { + accessor: '', + title: 'Actions', + render: (item: any) => ( + + + + + + + + + router.push(PATH_INVOICES.invoices.invoice_details(item.id)) + } + > + + + + + ), + }, + ]; + + useEffect(() => { + setPage(1); + }, [pageSize]); + + useEffect(() => { + const from = (page - 1) * pageSize; + const to = from + pageSize; + const d = sortBy(data, sortStatus.columnAccessor) as Invoices[]; + const dd = sortStatus.direction === 'desc' ? d.reverse() : d; + let filtered = dd.slice(from, to) as Invoices[]; + + if (debouncedQuery || selectedStatuses.length) { + filtered = data + .filter(({ full_name, status }) => { + if ( + debouncedQuery !== '' && + !full_name + .toLowerCase() + .includes(debouncedQuery.trim().toLowerCase()) + ) { + return false; + } + + // @ts-ignore + if ( + selectedStatuses.length && + !selectedStatuses.some((s) => s === status) + ) { + return false; + } + return true; + }) + .slice(from, to); + } + + setRecords(filtered); + }, [sortStatus, data, page, pageSize, debouncedQuery, selectedStatuses]); + + return error ? ( + + ) : ( + 0 + ? records.length + : data.length + } + recordsPerPage={pageSize} + page={page} + onPageChange={(p) => setPage(p)} + recordsPerPageOptions={PAGE_SIZES} + onRecordsPerPageChange={setPageSize} + sortStatus={sortStatus} + onSortStatusChange={setSortStatus} + fetching={loading} + /> + ); }; export default InvoicesTable; diff --git a/components/KanbanBoard/KanbanBoard.tsx b/components/KanbanBoard/KanbanBoard.tsx index c69e48c..1ea7107 100644 --- a/components/KanbanBoard/KanbanBoard.tsx +++ b/components/KanbanBoard/KanbanBoard.tsx @@ -1,356 +1,362 @@ -import {useMemo, useState} from "react"; +'use client'; + +import { useMemo, useState } from 'react'; import { - DndContext, - DragEndEvent, - DragOverEvent, - DragOverlay, - DragStartEvent, - PointerSensor, - useSensor, - useSensors, -} from "@dnd-kit/core"; -import {arrayMove, SortableContext} from "@dnd-kit/sortable"; -import {KanbanCard, KanbanColumn} from "@/components" -import {Id, KanbanColumn as IColumn, KanbanTask as ITask} from "@/types"; -import {IconNewSection, IconPlus} from "@tabler/icons-react"; -import {Box, Button, Portal, ScrollArea} from "@mantine/core"; -import {useMediaQuery} from "@mantine/hooks"; + DndContext, + DragEndEvent, + DragOverEvent, + DragOverlay, + DragStartEvent, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { arrayMove, SortableContext } from '@dnd-kit/sortable'; +import { KanbanCard, KanbanColumn } from '@/components'; +import { Id, KanbanColumn as IColumn, KanbanTask as ITask } from '@/types'; +import { IconNewSection, IconPlus } from '@tabler/icons-react'; +import { Box, Button, Portal, ScrollArea } from '@mantine/core'; +import { useMediaQuery } from '@mantine/hooks'; const defaultCols: IColumn[] = [ - { - id: "todo", - title: "Todo", - }, - { - id: "doing", - title: "Work in progress", - }, - { - id: "done", - title: "Done", - }, + { + id: 'todo', + title: 'Todo', + }, + { + id: 'doing', + title: 'Work in progress', + }, + { + id: 'done', + title: 'Done', + }, ]; const defaultTasks: ITask[] = [ - { - id: "1", - columnId: "todo", - content: "List admin APIs for dashboard", - comments: 0 - }, - { - id: "2", - columnId: "todo", - content: - "Develop user registration functionality with OTP delivered on SMS after email confirmation and phone number confirmation", - comments: 3 - }, - { - id: "3", - columnId: "doing", - content: "Conduct security testing", - comments: 2 - }, - { - id: "4", - columnId: "doing", - content: "Analyze competitors", - comments: 0 - }, - { - id: "5", - columnId: "done", - content: "Create UI kit documentation", - comments: 1 - }, - { - id: "6", - columnId: "done", - content: "Dev meeting", - comments: 4 - }, - { - id: "7", - columnId: "done", - content: "Deliver dashboard prototype", - comments: 15 - }, - { - id: "8", - columnId: "todo", - content: "Optimize application performance", - comments: 21 - }, - { - id: "9", - columnId: "todo", - content: "Implement data validation", - comments: 16 - }, - { - id: "10", - columnId: "todo", - content: "Design database schema", - comments: 56 - }, - { - id: "11", - columnId: "todo", - content: "Integrate SSL web certificates into workflow", - comments: 99 - }, - { - id: "12", - columnId: "doing", - content: "Implement error logging and monitoring", - comments: 76 - }, - { - id: "13", - columnId: "doing", - content: "Design and implement responsive UI", - comments: 45 - }, + { + id: '1', + columnId: 'todo', + content: 'List admin APIs for dashboard', + comments: 0, + }, + { + id: '2', + columnId: 'todo', + content: + 'Develop user registration functionality with OTP delivered on SMS after email confirmation and phone number confirmation', + comments: 3, + }, + { + id: '3', + columnId: 'doing', + content: 'Conduct security testing', + comments: 2, + }, + { + id: '4', + columnId: 'doing', + content: 'Analyze competitors', + comments: 0, + }, + { + id: '5', + columnId: 'done', + content: 'Create UI kit documentation', + comments: 1, + }, + { + id: '6', + columnId: 'done', + content: 'Dev meeting', + comments: 4, + }, + { + id: '7', + columnId: 'done', + content: 'Deliver dashboard prototype', + comments: 15, + }, + { + id: '8', + columnId: 'todo', + content: 'Optimize application performance', + comments: 21, + }, + { + id: '9', + columnId: 'todo', + content: 'Implement data validation', + comments: 16, + }, + { + id: '10', + columnId: 'todo', + content: 'Design database schema', + comments: 56, + }, + { + id: '11', + columnId: 'todo', + content: 'Integrate SSL web certificates into workflow', + comments: 99, + }, + { + id: '12', + columnId: 'doing', + content: 'Implement error logging and monitoring', + comments: 76, + }, + { + id: '13', + columnId: 'doing', + content: 'Design and implement responsive UI', + comments: 45, + }, ]; const KanbanBoard = () => { - const [columns, setColumns] = useState(defaultCols); - const columnsId = useMemo(() => columns.map((col) => col.id), [columns]); - const [tasks, setTasks] = useState(defaultTasks); - const [activeColumn, setActiveColumn] = useState(null); - const [activeTask, setActiveTask] = useState(null); - const tablet_match = useMediaQuery('(max-width: 768px)'); - - const sensors = useSensors( - useSensor(PointerSensor, { - activationConstraint: { - distance: 10, - }, - }) - ); - - return ( - (defaultCols); + const columnsId = useMemo(() => columns.map((col) => col.id), [columns]); + const [tasks, setTasks] = useState(defaultTasks); + const [activeColumn, setActiveColumn] = useState(null); + const [activeTask, setActiveTask] = useState(null); + const tablet_match = useMediaQuery('(max-width: 768px)'); + + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { + distance: 10, + }, + }), + ); + + return ( + + + - - - - - {columns.map((col) => ( - task.columnId === col.id)} - /> - ))} - - - - - - - - {activeColumn && ( - task.columnId === activeColumn.id - )} - /> - )} - {activeTask && ( - - )} - - - - - ); - - function createTask(columnId: Id) { - const newTask: ITask = { - id: generateId(), - columnId, - content: `Task ${tasks.length + 1}`, - }; - - setTasks([...tasks, newTask]); + + + {columns.map((col) => ( + task.columnId === col.id)} + /> + ))} + + + +
+ + + + {activeColumn && ( + task.columnId === activeColumn.id, + )} + /> + )} + {activeTask && ( + + )} + + + + + ); + + function createTask(columnId: Id) { + const newTask: ITask = { + id: generateId(), + columnId, + content: `Task ${tasks.length + 1}`, + }; + + setTasks([...tasks, newTask]); + } + + function deleteTask(id: Id) { + const newTasks = tasks.filter((task) => task.id !== id); + setTasks(newTasks); + } + + function updateTask(id: Id, content: string) { + const newTasks = tasks.map((task) => { + if (task.id !== id) return task; + return { ...task, content }; + }); + + setTasks(newTasks); + } + + function createNewColumn() { + const columnToAdd: IColumn = { + id: generateId(), + title: `Column ${columns.length + 1}`, + }; + + setColumns([...columns, columnToAdd]); + } + + function deleteColumn(id: Id) { + const filteredColumns = columns.filter((col) => col.id !== id); + setColumns(filteredColumns); + + const newTasks = tasks.filter((t) => t.columnId !== id); + setTasks(newTasks); + } + + function updateColumn(id: Id, title: string) { + const newColumns = columns.map((col) => { + if (col.id !== id) return col; + return { ...col, title }; + }); + + setColumns(newColumns); + } + + function onDragStart(event: DragStartEvent) { + if (event.active.data.current?.type === 'Column') { + setActiveColumn(event.active.data.current.column); + return; } - function deleteTask(id: Id) { - const newTasks = tasks.filter((task) => task.id !== id); - setTasks(newTasks); + if (event.active.data.current?.type === 'Task') { + setActiveTask(event.active.data.current.task); + return; } + } - function updateTask(id: Id, content: string) { - const newTasks = tasks.map((task) => { - if (task.id !== id) return task; - return {...task, content}; - }); + function onDragEnd(event: DragEndEvent) { + setActiveColumn(null); + setActiveTask(null); - setTasks(newTasks); - } + const { active, over } = event; + if (!over) return; - function createNewColumn() { - const columnToAdd: IColumn = { - id: generateId(), - title: `Column ${columns.length + 1}`, - }; + const activeId = active.id; + const overId = over.id; - setColumns([...columns, columnToAdd]); - } + if (activeId === overId) return; - function deleteColumn(id: Id) { - const filteredColumns = columns.filter((col) => col.id !== id); - setColumns(filteredColumns); + setColumns((columns) => { + const activeColumnIndex = columns.findIndex((col) => col.id === activeId); - const newTasks = tasks.filter((t) => t.columnId !== id); - setTasks(newTasks); - } + const overColumnIndex = columns.findIndex((col) => col.id === overId); - function updateColumn(id: Id, title: string) { - const newColumns = columns.map((col) => { - if (col.id !== id) return col; - return {...col, title}; - }); + return arrayMove(columns, activeColumnIndex, overColumnIndex); + }); + } - setColumns(newColumns); - } + function onDragOver(event: DragOverEvent) { + const { active, over } = event; + if (!over) return; - function onDragStart(event: DragStartEvent) { - if (event.active.data.current?.type === "Column") { - setActiveColumn(event.active.data.current.column); - return; - } - - if (event.active.data.current?.type === "Task") { - setActiveTask(event.active.data.current.task); - return; - } - } + const activeId = active.id; + const overId = over.id; - function onDragEnd(event: DragEndEvent) { - setActiveColumn(null); - setActiveTask(null); + if (activeId === overId) return; - const {active, over} = event; - if (!over) return; + const isActiveATask = active.data.current?.type === 'Task'; + const isOverATask = over.data.current?.type === 'Task'; - const activeId = active.id; - const overId = over.id; + if (!isActiveATask) return; - if (activeId === overId) return; + // Im dropping a Task over another Task + if (isActiveATask && isOverATask) { + setTasks((tasks) => { + const activeIndex = tasks.findIndex((t) => t.id === activeId); + const overIndex = tasks.findIndex((t) => t.id === overId); - setColumns((columns) => { - const activeColumnIndex = columns.findIndex((col) => col.id === activeId); + tasks[activeIndex].columnId = tasks[overIndex].columnId; - const overColumnIndex = columns.findIndex((col) => col.id === overId); - - return arrayMove(columns, activeColumnIndex, overColumnIndex); - }); + return arrayMove(tasks, activeIndex, overIndex); + }); } - function onDragOver(event: DragOverEvent) { - const {active, over} = event; - if (!over) return; - - const activeId = active.id; - const overId = over.id; - - if (activeId === overId) return; - - const isActiveATask = active.data.current?.type === "Task"; - const isOverATask = over.data.current?.type === "Task"; - - if (!isActiveATask) return; + const isOverAColumn = over.data.current?.type === 'Column'; - // Im dropping a Task over another Task - if (isActiveATask && isOverATask) { - setTasks((tasks) => { - const activeIndex = tasks.findIndex((t) => t.id === activeId); - const overIndex = tasks.findIndex((t) => t.id === overId); + // Im dropping a Task over a column + if (isActiveATask && isOverAColumn) { + setTasks((tasks) => { + const activeIndex = tasks.findIndex((t) => t.id === activeId); - tasks[activeIndex].columnId = tasks[overIndex].columnId; + tasks[activeIndex].columnId = overId; - return arrayMove(tasks, activeIndex, overIndex); - }); - } - - const isOverAColumn = over.data.current?.type === "Column"; - - // Im dropping a Task over a column - if (isActiveATask && isOverAColumn) { - setTasks((tasks) => { - const activeIndex = tasks.findIndex((t) => t.id === activeId); - - tasks[activeIndex].columnId = overId; - - return arrayMove(tasks, activeIndex, activeIndex); - }); - } + return arrayMove(tasks, activeIndex, activeIndex); + }); } -} + } +}; function generateId() { - /* Generate a random number between 0 and 10000 */ - return Math.floor(Math.random() * 10001); + /* Generate a random number between 0 and 10000 */ + return Math.floor(Math.random() * 10001); } export default KanbanBoard; diff --git a/components/KanbanCard/KanbanCard.module.css b/components/KanbanCard/KanbanCard.module.css new file mode 100644 index 0000000..b3ff237 --- /dev/null +++ b/components/KanbanCard/KanbanCard.module.css @@ -0,0 +1,35 @@ +.card { + border: 1px solid transparent; + + @mixin dark { + background-color: var(--mantine-color-dark-6); + } + + @mixin light { + background-color: var(--mantine-color-white); + } + + @mixin hover { + border-color: var(--mantine-color-blue-6); + } +} + +.dragBox { + position: relative; + min-height: rem(100); + display: flex; + align-items: center; + text-align: left; + cursor: grab; + border-radius: var(--mantine-radius-md); + + @mixin dark { + background-color: var(--mantine-color-gray-0); + opacity: 0.7; + } + + @mixin light { + background-color: var(--mantine-color-dark-0); + opacity: 0.3; + } +} diff --git a/components/KanbanCard/KanbanCard.stories.tsx b/components/KanbanCard/KanbanCard.stories.tsx new file mode 100644 index 0000000..ffd9fa0 --- /dev/null +++ b/components/KanbanCard/KanbanCard.stories.tsx @@ -0,0 +1,33 @@ +import type { StoryObj } from '@storybook/react'; + +import KanbanCard from './KanbanCard'; +import { Id } from '@/types'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +const meta = { + title: 'Tasks/Kanban/Card', + component: KanbanCard, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout + layout: 'centered', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + task: { + id: '1', + columnId: 'todo', + content: 'List admin APIs for dashboard', + comments: 0, + }, + deleteTask: (id: Id) => null, + updateTask: (id: Id, content: string) => null, + }, +}; diff --git a/components/KanbanCard/KanbanCard.styles.ts b/components/KanbanCard/KanbanCard.styles.ts deleted file mode 100644 index a12e8cf..0000000 --- a/components/KanbanCard/KanbanCard.styles.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {createStyles} from "@mantine/core"; - -export default createStyles((theme) => ({ - card: { - backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.white, - border: `1px solid transparent`, - paddingBottom: 0, - - '&:hover': { - borderColor: theme.colors[theme.primaryColor][7], - }, - }, -})); diff --git a/components/KanbanCard/KanbanCard.tsx b/components/KanbanCard/KanbanCard.tsx index fceedfb..62f39ec 100644 --- a/components/KanbanCard/KanbanCard.tsx +++ b/components/KanbanCard/KanbanCard.tsx @@ -1,239 +1,241 @@ -import {useState} from "react"; -import {Id, KanbanColumn as IColumn, KanbanTask as ITask} from "../../types"; -import {useSortable} from "@dnd-kit/sortable"; -import {CSS} from "@dnd-kit/utilities"; -import {IconDots, IconEdit, IconMessageCircle, IconTrash} from "@tabler/icons-react"; +'use client'; + +import { useState } from 'react'; +import { Id, KanbanTask as ITask } from '../../types'; +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { + IconDots, + IconEdit, + IconMessageCircle, + IconTrash, +} from '@tabler/icons-react'; import { - ActionIcon, - Avatar, - Box, - Button, Card, Divider, - Flex, Menu, - Paper, PaperProps, - rem, - Stack, - Text, - Textarea, - Tooltip, - useMantineTheme -} from "@mantine/core"; -import {useHover} from "@mantine/hooks"; -import useStyles from "./KanbanCard.styles" -import {modals} from "@mantine/modals"; + ActionIcon, + Avatar, + Box, + Button, + Card, + Divider, + Flex, + Menu, + Paper, + PaperProps, + Text, + Textarea, + Tooltip, + useMantineTheme, +} from '@mantine/core'; +import { useHover } from '@mantine/hooks'; +import { modals } from '@mantine/modals'; +import classes from './KanbanCard.module.css'; const AVATARS = [ - 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8cGVyc29ufGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60', - 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8cGVyc29ufGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60', - 'https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8cGVyc29ufGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60', + 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8cGVyc29ufGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60', + 'https://images.unsplash.com/flagged/photo-1570612861542-284f4c12e75f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8cGVyc29ufGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60', + 'https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8cGVyc29ufGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60', ]; const PAPER_PROPS: PaperProps = { - p: "md", - shadow: "md", - radius: "md", -} + p: 'md', + shadow: 'md', + radius: 'md', +}; -const ICON_SIZE = 18 +const ICON_SIZE = 18; type Props = { - task: ITask; - deleteTask: (id: Id) => void; - updateTask: (id: Id, content: string) => void; -} + task: ITask; + deleteTask: (id: Id) => void; + updateTask: (id: Id, content: string) => void; +}; const KanbanCard = (props: Props) => { - const {classes} = useStyles() - const {task, deleteTask, updateTask} = props; - const theme = useMantineTheme() - const [mouseIsOver, setMouseIsOver] = useState(false); - const [editMode, setEditMode] = useState(false); - const { - setNodeRef, - attributes, - listeners, - transform, - transition, - isDragging, - } = useSortable({ - id: task.id, - data: { - type: "Task", - task, - }, - disabled: editMode, - }); - const {hovered} = useHover(); - const style = { - transition, - transform: CSS.Transform.toString(transform), - }; + const { task, deleteTask, updateTask } = props; + const theme = useMantineTheme(); + const [mouseIsOver, setMouseIsOver] = useState(false); + const [editMode, setEditMode] = useState(false); + const { + setNodeRef, + attributes, + listeners, + transform, + transition, + isDragging, + } = useSortable({ + id: task.id, + data: { + type: 'Task', + task, + }, + disabled: editMode, + }); + const { hovered } = useHover(); + const style = { + transition, + transform: CSS.Transform.toString(transform), + }; - const toggleEditMode = () => { - setEditMode((prev) => !prev); - setMouseIsOver(false); - }; + const toggleEditMode = () => { + setEditMode((prev) => !prev); + setMouseIsOver(false); + }; - const confirmModal = (task: ITask) => modals.openConfirmModal({ - title: `Delete Task?`, - centered: true, - children: ( - - This task will be deleted - - ), - labels: {confirm: 'Delete bucket', cancel: "No don't delete it"}, - onCancel: () => console.log('Cancel'), - onConfirm: () => { - deleteTask(task.id); - }, + const confirmModal = (task: ITask) => + modals.openConfirmModal({ + title: `Delete Task?`, + centered: true, + children: This task will be deleted, + labels: { confirm: 'Delete bucket', cancel: "No don't delete it" }, + onCancel: () => console.log('Cancel'), + onConfirm: () => { + deleteTask(task.id); + }, }); - if (isDragging) { - return ( - - ); - } - - if (editMode) { - return ( - -