├── .env.example
├── .eslintrc.json
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
└── settings.json
├── README.md
├── bun.lockb
├── components.json
├── components
├── Feature.tsx
└── TopBar.tsx
├── drizzle.config.ts
├── next.config.mjs
├── package.json
├── pnpm-lock.yaml
├── postcss.config.cjs
├── public
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── card-01.png
├── card-02.png
├── card-03.png
├── ellipse-back.webp
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── git.svg
├── github-oss-animation.svg
├── images
│ ├── bridge.jpg
│ └── og-image.png
├── landing
│ ├── Record (online-voice-recorder.com).mp3
│ ├── feature
│ │ ├── feature1.webp
│ │ ├── feature2.webp
│ │ ├── feature3.webp
│ │ ├── feature4.webp
│ │ ├── feature5.webp
│ │ ├── feature6.webp
│ │ └── feature7.webp
│ ├── gradient-icon.webp
│ ├── gradient-icon2.webp
│ ├── icon-logos
│ │ ├── logo1.webp
│ │ ├── logo2.webp
│ │ ├── logo3.webp
│ │ ├── logo4.webp
│ │ ├── logo5.webp
│ │ ├── logo6.webp
│ │ └── logo7.webp
│ ├── lights
│ │ ├── horizontal-light.webp
│ │ ├── light-1 copy.webp
│ │ ├── light-1-srcset-1.png
│ │ ├── light-1-srcset-2.png
│ │ ├── light-1-webp
│ │ ├── light-2-srcset-1.png
│ │ ├── light-2-srcset-2.png
│ │ ├── light-2.webp
│ │ ├── light.webp
│ │ └── typescript-logo-svgrepo-com.svg
│ ├── powered
│ │ ├── shape.webp
│ │ └── shape2.svg
│ ├── right-bottom.webp
│ ├── right.png
│ └── usps
│ │ ├── 1.webp
│ │ ├── 2.webp
│ │ └── 3.webp
├── menu
│ ├── blog1.jpg
│ └── blog2.jpeg
├── next.svg
├── site.webmanifest
└── vercel.svg
├── src
├── app
│ ├── (non-dashboard)
│ │ ├── (auth)
│ │ │ ├── layout.tsx
│ │ │ ├── sign-in
│ │ │ │ └── page.tsx
│ │ │ └── sign-up
│ │ │ │ └── page.tsx
│ │ ├── (landing)
│ │ │ ├── _components
│ │ │ │ ├── Video
│ │ │ │ │ ├── Video.tsx
│ │ │ │ │ └── video.module.scss
│ │ │ │ ├── __development
│ │ │ │ │ └── font-showcase.tsx
│ │ │ │ ├── content-gradient
│ │ │ │ │ ├── content.module.scss
│ │ │ │ │ └── gradient-content.tsx
│ │ │ │ ├── cta-section
│ │ │ │ │ ├── CTAsection.module.scss
│ │ │ │ │ └── cta-section.tsx
│ │ │ │ ├── effects
│ │ │ │ │ ├── home-lights.tsx
│ │ │ │ │ ├── landing-effects.tsx
│ │ │ │ │ └── lights.tsx
│ │ │ │ ├── faq
│ │ │ │ │ ├── faq-item.tsx
│ │ │ │ │ └── faq.tsx
│ │ │ │ ├── feature
│ │ │ │ │ ├── _components
│ │ │ │ │ │ └── emoji-badges.tsx
│ │ │ │ │ ├── feature.module.scss
│ │ │ │ │ └── feature.tsx
│ │ │ │ ├── footer
│ │ │ │ │ ├── footer.d.ts
│ │ │ │ │ ├── footer.module.scss
│ │ │ │ │ └── footer.tsx
│ │ │ │ ├── hero
│ │ │ │ │ ├── ZenWealth
│ │ │ │ │ │ ├── Description.tsx
│ │ │ │ │ │ ├── Header.tsx
│ │ │ │ │ │ ├── ZenWealth.tsx
│ │ │ │ │ │ └── ZenWealthContainer.tsx
│ │ │ │ │ ├── hero.module.scss
│ │ │ │ │ └── hero.tsx
│ │ │ │ ├── horizontal-line.tsx
│ │ │ │ ├── icons.tsx
│ │ │ │ ├── logo-section
│ │ │ │ │ ├── logo-section.tsx
│ │ │ │ │ └── logos.module.scss
│ │ │ │ ├── navigation
│ │ │ │ │ ├── _components
│ │ │ │ │ │ ├── hero-badge.tsx
│ │ │ │ │ │ ├── logo.tsx
│ │ │ │ │ │ └── sign-in-button.tsx
│ │ │ │ │ ├── header.d.ts
│ │ │ │ │ ├── header.module.scss
│ │ │ │ │ └── header.tsx
│ │ │ │ ├── page-container.tsx
│ │ │ │ ├── powered-by
│ │ │ │ │ ├── Marquee.tsx
│ │ │ │ │ ├── carousel-brands.ts
│ │ │ │ │ ├── integration-title.module.scss
│ │ │ │ │ ├── integration-title.tsx
│ │ │ │ │ ├── powered-by.module.scss
│ │ │ │ │ └── powered-by.tsx
│ │ │ │ ├── rainbow-line.tsx
│ │ │ │ └── text-marquee
│ │ │ │ │ ├── MarqueeItems.tsx
│ │ │ │ │ ├── usp-marquee.tsx
│ │ │ │ │ └── video.module.scss
│ │ │ ├── b
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ └── styles
│ │ │ │ ├── app.scss
│ │ │ │ ├── landing.scss
│ │ │ │ ├── modules
│ │ │ │ ├── horizontal-line.module.css
│ │ │ │ ├── lights.module.scss
│ │ │ │ ├── noise.module.css
│ │ │ │ └── noise.png
│ │ │ │ ├── theme
│ │ │ │ ├── _fonts.scss
│ │ │ │ └── _typography.scss
│ │ │ │ └── utils
│ │ │ │ └── utillity-classes.scss
│ │ ├── (miscellaneous)
│ │ │ ├── changelog
│ │ │ │ ├── layout.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── design-system
│ │ │ │ ├── _components
│ │ │ │ │ ├── DesignSystemWrapper.tsx
│ │ │ │ │ └── DirectionAwareNav.tsx
│ │ │ │ ├── card-spotlight
│ │ │ │ │ └── page.tsx
│ │ │ │ └── notice
│ │ │ │ │ └── page.tsx
│ │ │ ├── hooks-showcase
│ │ │ │ ├── _components
│ │ │ │ │ └── hooks-showcase-wrapper.tsx
│ │ │ │ ├── geolocation
│ │ │ │ │ ├── _components
│ │ │ │ │ │ ├── geolocation-showcase.tsx
│ │ │ │ │ │ └── geolocation.loader.tsx
│ │ │ │ │ └── page.tsx
│ │ │ │ └── palette
│ │ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ └── old-landing
│ │ │ │ └── page.tsx
│ │ ├── docs
│ │ │ ├── kanban-board
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ └── todo
│ │ │ │ └── page.tsx
│ │ └── layout.tsx
│ ├── api
│ │ ├── account
│ │ │ └── route.ts
│ │ └── auth
│ │ │ ├── logout
│ │ │ └── route.ts
│ │ │ └── session
│ │ │ └── route.ts
│ ├── dashboard
│ │ ├── activity
│ │ │ └── page.tsx
│ │ ├── client-wrapper.tsx
│ │ ├── finance
│ │ │ ├── _components
│ │ │ │ ├── add-transaction-form.tsx
│ │ │ │ ├── display-goal.tsxpwd
│ │ │ │ ├── expense-breakdown.tsx
│ │ │ │ ├── finance-dashboard.tsx
│ │ │ │ ├── recent-transactions.tsx
│ │ │ │ ├── set-budget-goal.tsx
│ │ │ │ ├── set-goal-form.tsx
│ │ │ │ └── summary-card.tsx
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── folders
│ │ │ ├── _components
│ │ │ │ ├── file-tree.tsx
│ │ │ │ ├── folder-context-menu.tsx
│ │ │ │ └── new-folder-form.tsx
│ │ │ ├── _hooks
│ │ │ │ ├── updated-file-operations-hook.ts
│ │ │ │ └── use-file-operations.ts
│ │ │ └── page.tsx
│ │ ├── ig-follower-parser
│ │ │ ├── _components
│ │ │ │ └── csv-ig-parser.tsx
│ │ │ └── page.tsx
│ │ ├── layout.client.tsx
│ │ ├── layout.tsx
│ │ ├── media-recorder
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ ├── profile
│ │ │ ├── ProfileForm.tsx
│ │ │ ├── appearance.tsx
│ │ │ ├── get-user-profile.ts
│ │ │ └── page.tsx
│ │ ├── tasks
│ │ │ └── page.tsx
│ │ └── text-processor
│ │ │ ├── id-username
│ │ │ └── page.tsx
│ │ │ └── page.tsx
│ ├── favicon.ico
│ ├── layout.tsx
│ ├── loading.tsx
│ ├── not-found.tsx
│ ├── opengraph-image.png
│ ├── robots.ts
│ └── sitemap.ts
├── components
│ ├── Banner.tsx
│ ├── DeploymentInfo.tsx
│ ├── Sidebar.tsx
│ ├── ThemeProvider.tsx
│ ├── _development-utils
│ │ ├── auth-status-indicator.tsx
│ │ ├── color-picker.tsx
│ │ ├── color-utils.tsx
│ │ └── tailwind-color-palette.tsx
│ ├── _old-landing
│ │ ├── Hero
│ │ │ ├── Gradual-spacing.tsx
│ │ │ ├── Hero.tsx
│ │ │ ├── Maq.tsx
│ │ │ ├── Scrolls.tsx
│ │ │ └── ShinyLighs.tsx
│ │ ├── bent-grid.tsx
│ │ ├── bento-grid-parent.tsx
│ │ ├── bento-grid.config.ts
│ │ ├── bento-grid
│ │ │ ├── animated-beam-multiple-outputs.tsx
│ │ │ ├── animated-list-demo.tsx
│ │ │ ├── bento-grid-parent.tsx
│ │ │ ├── bento-grid.config.ts
│ │ │ └── bento-grid.tsx
│ │ ├── footer.tsx
│ │ └── spotlight-card
│ │ │ └── spotlight-cards.tsx
│ ├── aside
│ │ ├── route-specific
│ │ │ ├── dashboard-home-aside.tsx
│ │ │ ├── folders-aside.tsx
│ │ │ ├── ig-parsed-aside.tsx
│ │ │ ├── notes-aside.tsx
│ │ │ └── settings-aside.tsx
│ │ ├── sidebar.tsx
│ │ ├── site-settings-menu.tsx
│ │ ├── skeleton.sidebbar.tsx
│ │ ├── skeleton.sub-sidebar.tsx
│ │ ├── sub-sidebar-shell.tsx
│ │ └── types.sidear.d.ts
│ ├── atoms
│ │ ├── @@Kbd.tsx
│ │ ├── Center.tsx
│ │ ├── Flex.tsx
│ │ ├── Spacer.tsx
│ │ ├── index.ts
│ │ └── kbd.tsx
│ ├── auth
│ │ ├── auth.d.ts
│ │ ├── onboarding.tsx
│ │ ├── sign-in-form.tsx
│ │ ├── sign-out-button.tsx
│ │ ├── sign-up-form.tsx
│ │ ├── user-dropdown-menu
│ │ │ ├── user-dropdown-button.tsx
│ │ │ ├── user-dropdown-menu-item.tsx
│ │ │ └── user-dropdown-menu.tsx
│ │ └── with-user-info.tsx
│ ├── base
│ │ ├── Icons.tsx
│ │ ├── ThemeWrapper.tsx
│ │ ├── icons.tsx
│ │ ├── layout
│ │ │ └── main-content-wrapper.tsx
│ │ └── logo.tsx
│ ├── dashboard
│ │ ├── features
│ │ │ ├── activity
│ │ │ │ ├── activities.d.ts
│ │ │ │ ├── activities.tsx
│ │ │ │ ├── activity-card.tsx
│ │ │ │ ├── activity-feed.tsx
│ │ │ │ ├── activity-list.tsx
│ │ │ │ ├── animate-height.tsx
│ │ │ │ ├── create-activity-form.tsx
│ │ │ │ ├── create-activity-popover.tsx
│ │ │ │ ├── mock-activity-api.ts
│ │ │ │ ├── recent-activities.tsx
│ │ │ │ └── use-activities.ts
│ │ │ ├── intro
│ │ │ │ ├── current-time.tsx
│ │ │ │ └── get-weather.tsx
│ │ │ └── tasks
│ │ │ │ ├── create-task-popover.tsx
│ │ │ │ ├── task-board.tsx
│ │ │ │ ├── task-detail-popover.tsx
│ │ │ │ ├── task-item.tsx
│ │ │ │ ├── task-list.tsx
│ │ │ │ └── task-management.tsx
│ │ ├── layout
│ │ │ └── top-bar
│ │ │ │ └── top-bar.tsx
│ │ ├── no-data-state.tsx
│ │ ├── onboarding-trigger.tsx
│ │ └── weather-time.tsx
│ ├── effects
│ │ ├── animated-beam.tsx
│ │ ├── animated-list.tsx
│ │ ├── blur-in.tsx
│ │ ├── card-spotlight
│ │ │ ├── card-spotlight.tsx
│ │ │ ├── use-mouse-position.ts
│ │ │ └── useMousePosition.ts
│ │ ├── confetti.tsx
│ │ ├── empty-state-loader.tsx
│ │ ├── fade-in.tsx
│ │ ├── gitihu-oss
│ │ │ ├── ShinyLighs.tsx
│ │ │ ├── github-open-source.tsx
│ │ │ ├── oss-chips.tsx
│ │ │ └── oss-light.tsx
│ │ ├── globe
│ │ │ └── world-globe.tsx
│ │ ├── glow-text-effect.tsx
│ │ ├── hover-card.tsx
│ │ ├── loaders
│ │ │ ├── heartbeat-loader.tsx
│ │ │ ├── loading-dots.tsx
│ │ │ ├── skeleton-loader.tsx
│ │ │ └── skeleton.tsx
│ │ ├── number-ticker.tsx
│ │ ├── onboarding-trigger.tsx
│ │ ├── particles.tsx
│ │ ├── ripple.tsx
│ │ ├── shiny-button.tsx
│ │ ├── show-hide.tsx
│ │ ├── sortable-list.tsx
│ │ └── spotlight.txt
│ ├── elements
│ │ ├── crud
│ │ │ ├── add-note-modal.tsx
│ │ │ ├── confirm-dialog.tsx
│ │ │ ├── confirmation-modal.tsx
│ │ │ ├── create-folder-modal.tsx
│ │ │ ├── crud-button.tsx
│ │ │ └── edit-folder-modal.tsx
│ │ ├── custom-dropdown.tsx
│ │ ├── dark-light-toggle.tsx
│ │ ├── display-code
│ │ │ ├── code-block.tsx
│ │ │ ├── code-highlight
│ │ │ │ ├── CodeContent.tsx
│ │ │ │ ├── FileHeader.tsx
│ │ │ │ ├── code-highlight.tsx
│ │ │ │ └── types.code-highlight.ts
│ │ │ └── command-inline-code.tsx
│ │ ├── index.ts
│ │ ├── notice-box.tsx
│ │ ├── notification-bar
│ │ │ └── notification-bar.tsx
│ │ ├── search.tsx
│ │ └── tree-renderer.tsx
│ ├── features
│ │ ├── record-peripherals
│ │ │ ├── audio-recorder.tsx
│ │ │ ├── display-webcam.tsx
│ │ │ ├── peripherals-recorder.tsx
│ │ │ ├── video-recorder.tsx
│ │ │ └── volume-slider.tsx
│ │ └── text-processor
│ │ │ ├── text-processor copy.tsx
│ │ │ └── text-processor.tsx
│ ├── rich-text-editor.tsx
│ ├── sidebar-items.tsx
│ ├── tiptap-editor.tsx
│ └── ui
│ │ ├── ThemeToggle.tsx
│ │ ├── accordion.tsx
│ │ ├── alert-dialog.tsx
│ │ ├── alert.tsx
│ │ ├── animations.ts
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── breadcrumb.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── cals.tsx
│ │ ├── card.tsx
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── color-picker.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dialog.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── form.tsx
│ │ ├── hover-card.tsx
│ │ ├── icon.tsx
│ │ ├── index.ts
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── marquee.tsx
│ │ ├── menubar.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── notice.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── popup.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── resizable.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── simple-checkbox.tsx
│ │ ├── skeleton.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── spinner.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea.tsx
│ │ ├── toaster.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ ├── tooltip.tsx
│ │ └── useFormField.tsx
├── config
│ ├── menu-config.ts
│ ├── nav.ts
│ └── site-config.ts
├── core
│ ├── config
│ │ ├── fonts
│ │ │ ├── font.bricolage_grotesque.ts
│ │ │ ├── font.pt_mono.ts
│ │ │ ├── fonts.ts
│ │ │ └── geist
│ │ │ │ ├── GeistMonoVF.woff
│ │ │ │ ├── GeistVF.woff
│ │ │ │ └── font.geist.ts
│ │ ├── locales
│ │ │ ├── en.ts
│ │ │ └── locales.ts
│ │ ├── menu-items
│ │ │ ├── dashboard-navigation-menu-items.ts
│ │ │ ├── design-system-menu.items.ts
│ │ │ ├── dropdown-menu.items.ts
│ │ │ └── sidebar-menu-items.ts
│ │ ├── metadata
│ │ │ ├── dashboard.metadata.ts
│ │ │ └── metadata.root-layout.ts
│ │ ├── nav.ts
│ │ └── site-config.ts
│ ├── constants
│ │ ├── animations.ts
│ │ ├── cn-tw.ts
│ │ └── generate-uuid.ts
│ ├── hooks
│ │ ├── _hook_helpers
│ │ │ ├── use-event-callback.ts
│ │ │ ├── use-event-listener.ts
│ │ │ └── use-isomorphic-layout-effect.ts
│ │ ├── index.ts
│ │ ├── use-copy-clipboard.ts
│ │ ├── use-dev-mode.ts
│ │ ├── use-geo-location.ts
│ │ ├── use-keyboard-shortcuts.ts
│ │ ├── use-local-storage.ts
│ │ ├── use-mouse-hover.ts
│ │ ├── use-skeleton-loader.ts
│ │ └── use-user-info.ts
│ ├── models
│ │ ├── activities.ts
│ │ ├── folders.z.ts
│ │ └── index.ts
│ ├── scripts
│ │ └── fix-imports
│ │ │ ├── optimize-iu-imports.mdx
│ │ │ └── optimize-ui-imports.py
│ ├── server
│ │ ├── actions
│ │ │ ├── activities
│ │ │ │ └── create-activity.ts
│ │ │ ├── activity-log
│ │ │ │ ├── fetch-activity.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── log-activity.ts
│ │ │ ├── activity.ts
│ │ │ ├── finance
│ │ │ │ ├── budget.ts
│ │ │ │ ├── goals.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── transactions.ts
│ │ │ ├── folders
│ │ │ │ └── index.ts
│ │ │ ├── get-visitory-city.ts
│ │ │ ├── github
│ │ │ │ ├── fetch-github-stats.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── notes.ts
│ │ │ ├── save-parsed-output.ts
│ │ │ ├── save-processed-text
│ │ │ │ └── save-processed-text.ts
│ │ │ ├── tasks
│ │ │ │ ├── add-sub-task.ts
│ │ │ │ ├── add-task-label.ts
│ │ │ │ ├── create-task.ts
│ │ │ │ ├── get-label.ts
│ │ │ │ ├── get-task.ts
│ │ │ │ ├── index.tsx
│ │ │ │ ├── remove-task-label.ts
│ │ │ │ ├── update-sub-task.ts
│ │ │ │ ├── update-task-due-date.ts
│ │ │ │ ├── update-task-priority.ts
│ │ │ │ └── update-task-status.ts
│ │ │ ├── update-user-profile.ts
│ │ │ └── user
│ │ │ │ ├── action.sign-in.ts
│ │ │ │ ├── action.sign-out.ts
│ │ │ │ ├── action.sign-up.ts
│ │ │ │ ├── action.update-user.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── user-profile.ts
│ │ ├── auth
│ │ │ ├── client-auth-utils.ts
│ │ │ ├── lucia.ts
│ │ │ └── utils.ts
│ │ └── db
│ │ │ ├── index.ts
│ │ │ └── schema
│ │ │ ├── activity.ts
│ │ │ ├── auth.ts
│ │ │ ├── finance
│ │ │ ├── budget.ts
│ │ │ ├── goals.ts
│ │ │ ├── index.ts
│ │ │ └── transactions.ts
│ │ │ ├── folders.ts
│ │ │ ├── index.ts
│ │ │ ├── notes.ts
│ │ │ ├── parsed-ig.ts
│ │ │ ├── processed-text.ts
│ │ │ └── tasks
│ │ │ ├── index.ts
│ │ │ ├── label.ts
│ │ │ ├── task-label.ts
│ │ │ └── task.ts
│ └── stores
│ │ ├── index.ts
│ │ ├── store.dismiss-notification.ts
│ │ ├── store.file-tree.ts
│ │ ├── store.main.sidebar.ts
│ │ ├── store.notes.ts
│ │ ├── store.parsed.ts
│ │ ├── store.site-settings.ts
│ │ ├── store.sub-sidebar.ts
│ │ ├── store.theme.ts
│ │ └── store.toasts.ts
├── lib
│ ├── actions
│ │ ├── folders.ts
│ │ └── notes.ts
│ ├── env.mjs
│ └── utils.ts
├── styles
│ ├── app.scss
│ ├── components
│ │ ├── _hover-efffect.scss
│ │ ├── _mouse-grid.scss
│ │ └── _scrollar.scss
│ ├── core
│ │ └── _typography.scss
│ ├── themes
│ │ ├── _default-theme.scss
│ │ └── _theme-selector.scss
│ └── utillities
│ │ ├── _animations.scss
│ │ ├── _border.scss
│ │ └── _utillities.scss
└── types
│ ├── canvas-confetti.d.ts
│ ├── design-system.d.ts
│ ├── global.d.ts
│ ├── tasks.d.ts
│ ├── tree-item.types.d.ts
│ ├── types.folder.d.ts
│ └── types.users.d.ts
├── tailwind.config.ts
└── tsconfig.json
/.env.example:
--------------------------------------------------------------------------------
1 | # Database Configuration
2 | DATABASE_URL=
3 |
4 | # GitHub Configuration
5 | GITHUB_ACCESS_TOKEN=
6 | NEXT_PUBLIC_GITHUB_USERNAME=
7 | NEXT_PUBLIC_GITHUB_REPOSITORY=
8 |
9 | # OpenRouteService API Key
10 | OPENROUTE_SERVICE_API_KEY=
11 |
12 | # IP Geolocation API Key
13 | IP_GEOLOCATION_API_KEY=
14 |
15 | # Admin Email Configuration
16 | ADMIN_EMAIL=
17 | ADMIN_EMAIL_TWO=
18 |
19 | # GitHub and Vercel Tokens for Changelog
20 | NEXT_PUBLIC_GITHUB_TOKEN=
21 | NEXT_PUBLIC_VERCEL_PROJECT_ID=
22 | NEXT_PUBLIC_VERCEL_TOKEN=
23 |
24 | # PostHog Analytics Configuration
25 | NEXT_PUBLIC_POSTHOG_KEY=
26 | NEXT_PUBLIC_POSTHOG_HOST=
27 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals",
3 | "rules": {
4 | "react/jsx-no-undef": [
5 | "error",
6 | {
7 | "ignore": ["spa", "title", "ptitle", "subtitle", "psub"]
8 | }
9 | ]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 | .env
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
39 |
40 |
41 | # package managers
42 | bun.lockb
43 |
44 | # misc
45 | .cursorrules
46 | .cursorignore
47 | _LOCAL/
48 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .next
3 | dist
4 | build
5 | .contentlayer
6 | public
7 | tmp
8 | _templates
9 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "tabWidth": 4,
4 | "singleQuote": true,
5 | "semi": false,
6 | "trailingComma": "none",
7 | "overrides": [
8 | {
9 | "files": ["*.tsx", "*.ts", "*.scss", "*.css", "*.mdx"],
10 | "options": {
11 | "useTabs": true,
12 | "tabWidth": 4,
13 | "singleQuote": true,
14 | "semi": false,
15 | "trailingComma": "none"
16 | }
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "git.ignoreLimitWarning": true,
3 | "typescript.tsdk": "node_modules/typescript/lib",
4 | "keybindings": [
5 | {
6 | "key": "ctrl+u",
7 | "command": "workbench.action.chat.startVoiceChat",
8 | "when": "!voiceChatInProgress"
9 | },
10 | {
11 | "key": "ctrl+u",
12 | "command": "workbench.action.chat.stopListeningAndSubmit",
13 | "when": "voiceChatInProgress"
14 | },
15 | {
16 | "key": "ctrl+d",
17 | "command": "workbench.action.editorDictation.start",
18 | "when": "!editorDictation.inProgress"
19 | },
20 | {
21 | "key": "ctrl+d",
22 | "command": "workbench.action.editorDictation.stop",
23 | "when": "editorDictation.inProgress"
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/bun.lockb
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "src/app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true
11 | },
12 | "aliases": {
13 | "components": "@/components",
14 | "utils": "@/lib/utils"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/components/Feature.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/components/Feature.tsx
--------------------------------------------------------------------------------
/components/TopBar.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/components/TopBar.tsx
--------------------------------------------------------------------------------
/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import { env } from '@/lib/env.mjs'
2 | import type { Config } from 'drizzle-kit'
3 |
4 | export default {
5 | schema: './src/lib/db/schema/index.ts',
6 | out: './src/core/server/db/migrations',
7 | driver: 'pg',
8 | dbCredentials: {
9 | connectionString: env.DATABASE_URL
10 | }
11 | } satisfies Config
12 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | experimental: {
4 | reactCompiler: true
5 | },
6 | images: {
7 | domains: ['cdn.prod.website-files.com']
8 | },
9 | webpack: config => {
10 | config.externals.push('@node-rs/argon2', '@node-rs/bcrypt')
11 | return config
12 | },
13 | eslint: {
14 | ignoreDuringBuilds: true
15 | },
16 | typescript: {
17 | ignoreBuildErrors: true
18 | }
19 | }
20 |
21 | export default nextConfig
22 |
--------------------------------------------------------------------------------
/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/card-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/card-01.png
--------------------------------------------------------------------------------
/public/card-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/card-02.png
--------------------------------------------------------------------------------
/public/card-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/card-03.png
--------------------------------------------------------------------------------
/public/ellipse-back.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/ellipse-back.webp
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/bridge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/images/bridge.jpg
--------------------------------------------------------------------------------
/public/images/og-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/images/og-image.png
--------------------------------------------------------------------------------
/public/landing/Record (online-voice-recorder.com).mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/Record (online-voice-recorder.com).mp3
--------------------------------------------------------------------------------
/public/landing/feature/feature1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature1.webp
--------------------------------------------------------------------------------
/public/landing/feature/feature2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature2.webp
--------------------------------------------------------------------------------
/public/landing/feature/feature3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature3.webp
--------------------------------------------------------------------------------
/public/landing/feature/feature4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature4.webp
--------------------------------------------------------------------------------
/public/landing/feature/feature5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature5.webp
--------------------------------------------------------------------------------
/public/landing/feature/feature6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature6.webp
--------------------------------------------------------------------------------
/public/landing/feature/feature7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/feature/feature7.webp
--------------------------------------------------------------------------------
/public/landing/gradient-icon.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/gradient-icon.webp
--------------------------------------------------------------------------------
/public/landing/gradient-icon2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/gradient-icon2.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo1.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo2.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo3.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo4.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo5.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo6.webp
--------------------------------------------------------------------------------
/public/landing/icon-logos/logo7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/icon-logos/logo7.webp
--------------------------------------------------------------------------------
/public/landing/lights/horizontal-light.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/horizontal-light.webp
--------------------------------------------------------------------------------
/public/landing/lights/light-1 copy.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-1 copy.webp
--------------------------------------------------------------------------------
/public/landing/lights/light-1-srcset-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-1-srcset-1.png
--------------------------------------------------------------------------------
/public/landing/lights/light-1-srcset-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-1-srcset-2.png
--------------------------------------------------------------------------------
/public/landing/lights/light-1-webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-1-webp
--------------------------------------------------------------------------------
/public/landing/lights/light-2-srcset-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-2-srcset-1.png
--------------------------------------------------------------------------------
/public/landing/lights/light-2-srcset-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-2-srcset-2.png
--------------------------------------------------------------------------------
/public/landing/lights/light-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light-2.webp
--------------------------------------------------------------------------------
/public/landing/lights/light.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/lights/light.webp
--------------------------------------------------------------------------------
/public/landing/lights/typescript-logo-svgrepo-com.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/landing/powered/shape.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/powered/shape.webp
--------------------------------------------------------------------------------
/public/landing/powered/shape2.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/landing/right-bottom.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/right-bottom.webp
--------------------------------------------------------------------------------
/public/landing/right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/right.png
--------------------------------------------------------------------------------
/public/landing/usps/1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/usps/1.webp
--------------------------------------------------------------------------------
/public/landing/usps/2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/usps/2.webp
--------------------------------------------------------------------------------
/public/landing/usps/3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/landing/usps/3.webp
--------------------------------------------------------------------------------
/public/menu/blog1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/menu/blog1.jpg
--------------------------------------------------------------------------------
/public/menu/blog2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/public/menu/blog2.jpeg
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(auth)/layout.tsx:
--------------------------------------------------------------------------------
1 | import { getUserAuth } from '@/core/server/auth/utils'
2 |
3 | import { redirect } from 'next/navigation'
4 |
5 | export default async function AuthLayout({
6 | children
7 | }: {
8 | children: React.ReactNode
9 | }) {
10 | const session = await getUserAuth()
11 | if (session?.session) redirect('/dashboard')
12 |
13 | return (
14 |
{children}
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/effects/home-lights.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { motion } from 'framer-motion'
4 |
5 | export default function HomeLights() {
6 | return (
7 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/effects/lights.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/app/(non-dashboard)/(landing)/_components/effects/lights.tsx
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/feature/_components/emoji-badges.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface FeatureBadge {
4 | icon: string
5 | text: string
6 | }
7 |
8 | const featureBadges: FeatureBadge[] = [
9 | { icon: '✨', text: 'Personable' },
10 | { icon: '🫠', text: 'Empathetic' },
11 | { icon: '🎯', text: 'Direct' },
12 | { icon: '😇', text: 'Friendly' }
13 | ]
14 |
15 | const FeatureBadges: React.FC = () => {
16 | return (
17 |
18 | {featureBadges.map((badge, index) => (
19 |
23 |
24 | {badge.icon}
25 |
26 |
{badge.text}
27 |
28 | ))}
29 |
30 | )
31 | }
32 |
33 | export default FeatureBadges
34 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/footer/footer.d.ts:
--------------------------------------------------------------------------------
1 | export type FooterLink = {
2 | text: string
3 | href: string
4 | isNew?: boolean
5 | isBeta?: boolean
6 | external?: boolean
7 | }
8 |
9 | export type FooterColumnProps = {
10 | title: string
11 | links: FooterLink[]
12 | }
13 |
14 | export type FooterContactLinkProps = {
15 | href: string
16 | icon: React.ReactNode
17 | text: string
18 | target?: string
19 | external?: boolean
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/hero/ZenWealth/Description.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | type DescriptionProps = {
4 | text: string[]
5 | }
6 |
7 | export function Description({ text }: DescriptionProps) {
8 | return (
9 |
10 | {text.map((line, index) => (
11 |
12 | {line}
13 |
14 |
15 | ))}
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/hero/ZenWealth/Header.tsx:
--------------------------------------------------------------------------------
1 | type HeaderProps = {
2 | title: string
3 | subtitle: string
4 | }
5 |
6 | export function Header({ title, subtitle }: HeaderProps) {
7 | return (
8 |
9 |
10 | {title}
11 |
12 | {subtitle}
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/hero/ZenWealth/ZenWealth.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { easeInOut, motion } from 'framer-motion'
4 | import HeroBadge from '../../navigation/_components/hero-badge'
5 | import { Description } from './Description'
6 | import { Header } from './Header'
7 |
8 | type ZenWealthProps = {
9 | title: string
10 | subtitle: string
11 | description: string[]
12 | }
13 |
14 | export function ZenWealth({ title, subtitle, description }: ZenWealthProps) {
15 | return (
16 |
17 |
18 |
23 |
24 |
25 |
34 |
35 |
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/hero/ZenWealth/ZenWealthContainer.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { ZenWealth } from './ZenWealth'
4 |
5 | function getZenWealthData() {
6 | return {
7 | title: 'Zen is wealth.',
8 | subtitle: 'A central hub for digital life.',
9 | description: [
10 | 'Built by a dev, for myself. Without the known annoyance',
11 | 'of having to wait for Cloudflare security checks,',
12 | 'ads, newsletter popups or reCAPTCHAs.'
13 | ]
14 | }
15 | }
16 |
17 | export function ZenWealthContainer() {
18 | const zenWealthData = getZenWealthData()
19 |
20 | return (
21 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/hero/hero.module.scss:
--------------------------------------------------------------------------------
1 | .videoContainerLines {
2 | z-index: 5;
3 | width: 100%;
4 | max-width: 1128px;
5 | margin-left: auto;
6 | margin-right: auto;
7 | padding-top: 72px;
8 | padding-bottom: 72px;
9 | position: relative;
10 | }
11 |
12 | .linesGroup {
13 | z-index: 2;
14 | pointer-events: none;
15 | position: absolute;
16 | top: 0%;
17 | bottom: 0%;
18 | left: 0%;
19 | right: 0%;
20 | }
21 |
22 | .lineVerticalLeft,
23 | .lineVerticalRight {
24 | z-index: 1;
25 | background-color: var(--neutral--800);
26 | width: 1px;
27 | height: auto;
28 | position: absolute;
29 | top: 4.5px;
30 | bottom: 0%;
31 | }
32 |
33 | .lineVerticalLeft {
34 | left: 0%;
35 | right: auto;
36 | }
37 |
38 | .lineVerticalRight {
39 | left: auto;
40 | right: 0%;
41 | }
42 |
43 | .lineDot {
44 | z-index: 5;
45 | border: 1px solid var(--neutral--800);
46 | background-color: #0d0d11;
47 | border-radius: 2px;
48 | width: 10px;
49 | height: 10px;
50 | position: absolute;
51 | }
52 |
53 | .bottomLeft {
54 | top: auto;
55 | bottom: -4.5px;
56 | left: -4.5px;
57 | right: auto;
58 | }
59 |
60 | .bottomRight {
61 | top: auto;
62 | bottom: -4.5px;
63 | left: auto;
64 | right: -4.5px;
65 | }
66 |
67 | .topLeft {
68 | top: -4.5px;
69 | bottom: auto;
70 | left: -4.5px;
71 | right: auto;
72 | }
73 |
74 | .topRight {
75 | top: -4.5px;
76 | bottom: auto;
77 | left: auto;
78 | right: -4.5px;
79 | }
80 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/hero/hero.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 | import HorizontalLine from '../horizontal-line'
3 | import { ZenWealthContainer } from './ZenWealth/ZenWealthContainer'
4 | import styles from './hero.module.scss'
5 |
6 | type LineDotProps = {
7 | position: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight'
8 | }
9 |
10 | function LineDot({ position }: LineDotProps) {
11 | return
12 | }
13 |
14 | export default function Hero() {
15 | return (
16 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/horizontal-line.tsx:
--------------------------------------------------------------------------------
1 | import styles from '../styles/modules/horizontal-line.module.css'
2 |
3 | export default function HorizontalLine() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/navigation/_components/logo.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import RainbowLine from '../../rainbow-line'
3 | import styles from '../header.module.scss'
4 |
5 | type LogoProps = {
6 | className?: string
7 | isScrolled: boolean
8 | }
9 |
10 | export default function Logo({ className, isScrolled }: LogoProps) {
11 | const name = 'remco.'
12 |
13 | return (
14 |
18 |
21 | {name}
22 |
23 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | function LogoTitle({
33 | children,
34 | className
35 | }: {
36 | children: React.ReactNode
37 | className?: string
38 | }) {
39 | return (
40 |
41 | {children}
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/navigation/_components/sign-in-button.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 |
3 | type SecondaryButtonProps = {
4 | href: string
5 | children: React.ReactNode
6 | className?: string
7 | }
8 |
9 | const SecondaryButton: React.FC = ({
10 | href,
11 | children,
12 | className
13 | }) => {
14 | return (
15 |
19 |
20 | {children}
21 |
22 |
23 | )
24 | }
25 |
26 | export default SecondaryButton
27 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/navigation/header.d.ts:
--------------------------------------------------------------------------------
1 | export type ProductCategoryProps = {
2 | name: string
3 | links: Array<{
4 | href: string
5 | external?: boolean
6 | name: string
7 | }>
8 | }
9 |
10 | export type BlogPostProps = {
11 | id: string
12 | imageSrc: string
13 | title: string
14 | description: string
15 | }
16 |
17 | export type AuthLinkProps = {
18 | href: string
19 | Icon: React.ComponentType
20 | text: string
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/powered-by/Marquee.tsx:
--------------------------------------------------------------------------------
1 | import Marquee from '@/components/ui/marquee'
2 | import React from 'react'
3 | import { carouselbrands } from './carousel-brands'
4 |
5 | interface CarouselItemProps {
6 | iconSrc: string
7 | altText: string
8 | text: string
9 | }
10 |
11 | const CarouselItem: React.FC = ({
12 | iconSrc,
13 | altText,
14 | text
15 | }) => {
16 | return (
17 |
18 |

24 |
{text}
25 |
26 | )
27 | }
28 |
29 | export default function MarqueeDemo({
30 | reverse = false
31 | }: {
32 | reverse?: boolean
33 | }) {
34 | return (
35 |
36 |
37 |
38 | {' '}
39 |
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/powered-by/carousel-brands.ts:
--------------------------------------------------------------------------------
1 | export const carouselbrands = [
2 | {
3 | iconSrc:
4 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd507261f4d64d555c90_Apple%20Icon.webp',
5 | text: 'Apple'
6 | },
7 | {
8 | iconSrc:
9 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd50800bda510d0058d4_Slack%20Icon.webp',
10 | text: 'Slack'
11 | },
12 | {
13 | iconSrc:
14 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd5094b722f62744d5f9_Discord%20Icon.webp',
15 | text: 'Discord'
16 | },
17 | {
18 | iconSrc:
19 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd5089af154f1bcc3356_Klarna%20Icon.webp',
20 | text: 'Klarna'
21 | },
22 | {
23 | iconSrc:
24 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd5035c3344bd742b7c2_Amazon%20Icon.webp',
25 | text: 'Amazon'
26 | },
27 | {
28 | iconSrc:
29 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd5089af154f1bcc3364_Meta%20Icon.webp',
30 | text: 'Meta'
31 | },
32 | {
33 | iconSrc:
34 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd5189af154f1bcc3420_Yelp%20Icon.webp',
35 | text: 'Yelp'
36 | },
37 | {
38 | iconSrc:
39 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd51800bda510d005915_Xing%20Icon.webp',
40 | text: 'Xing'
41 | },
42 | {
43 | iconSrc:
44 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd50fca0ab56c8130f39_Twitch%20Icon.webp',
45 | text: 'Twitch'
46 | },
47 | {
48 | iconSrc:
49 | 'https://cdn.prod.website-files.com/65b8e9cb3c15d9b62f057c9a/65bdfd5096bc5b82059cbe01_Android%20Icon.webp',
50 | text: 'Android'
51 | }
52 | ]
53 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/powered-by/integration-title.module.scss:
--------------------------------------------------------------------------------
1 | .integrationTitle {
2 | grid-column-gap: 12px;
3 | grid-row-gap: 12px;
4 | text-align: center;
5 | flex-direction: column;
6 | align-items: center;
7 | width: 100%;
8 | max-width: 552px;
9 | margin-bottom: 100px;
10 | margin-left: auto;
11 | margin-right: auto;
12 | display: flex;
13 | transform: translate3d(0px, 0px, 0px) scale3d(1, 1, 1) rotateX(0deg)
14 | rotateY(0deg) rotateZ(0deg) skew(0deg, 0deg);
15 | opacity: 1;
16 | transform-style: preserve-3d;
17 | }
18 |
19 | .badge {
20 | grid-column-gap: 4px;
21 | grid-row-gap: 4px;
22 | border: 1px solid var(--white--6);
23 | background-color: var(--white--2);
24 | box-shadow: inset 0 -4px 12px 0 var(--white--10);
25 | backdrop-filter: blur(12px);
26 | color: var(--base--white);
27 | border-radius: 99px;
28 | align-items: center;
29 | padding: 4px 10px;
30 | font-size: 12px;
31 | line-height: 1.7;
32 | display: flex;
33 | }
34 |
35 | .integrationTitle h3 {
36 | margin-top: 20px;
37 | margin-bottom: 10px;
38 | color: var(--base--white);
39 | letter-spacing: -0.17px;
40 | font-family: 'Bricolage Grotesque', sans-serif;
41 | font-size: 40px;
42 | font-weight: 500;
43 | line-height: 1.2;
44 | }
45 |
46 | .gradientSpan {
47 | background-image: linear-gradient(
48 | to bottom,
49 | var(--base--white),
50 | var(--white--64)
51 | );
52 | -webkit-text-fill-color: transparent;
53 | background-clip: text;
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/powered-by/integration-title.tsx:
--------------------------------------------------------------------------------
1 | import styles from './integration-title.module.scss'
2 |
3 | export default function IntegrationTitle() {
4 | return (
5 |
6 |
7 |
Application powered by
8 |
9 |
10 |
11 | You won't get AI pushed
12 |
down your throat here
13 |
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/_components/text-marquee/MarqueeItems.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { easeInOut, motion } from 'framer-motion'
4 | import HorizontalLine from '../horizontal-line'
5 | import UspMarquee from './usp-marquee'
6 | import styles from './video.module.scss'
7 | type LineDotProps = {
8 | position: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight'
9 | }
10 |
11 | function LineDot({ position }: LineDotProps) {
12 | return
13 | }
14 |
15 | function LinesGroup() {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default function MarqueeItems() {
29 | return (
30 |
31 |
32 |
33 |
39 |
40 |
41 |
47 |
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/layout.tsx:
--------------------------------------------------------------------------------
1 | import HomeLights from './_components/effects/home-lights'
2 | import LandingEffects from './_components/effects/landing-effects'
3 | // import './styles/landing.scss'
4 | import './styles/app.scss'
5 |
6 | export default function DashboardLayout({
7 | children
8 | }: {
9 | children: React.ReactNode
10 | }) {
11 | return (
12 | <>
13 |
14 | {children}
15 |
16 | >
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/styles/modules/horizontal-line.module.css:
--------------------------------------------------------------------------------
1 | .horizontalLine {
2 | z-index: 0;
3 | background-color: var(--neutral--800);
4 | pointer-events: none;
5 | width: 100vw;
6 | height: 1px;
7 | position: absolute;
8 | top: auto;
9 | bottom: 0%;
10 | left: 50%;
11 | transform: translateX(-50%);
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/styles/modules/lights.module.scss:
--------------------------------------------------------------------------------
1 | .home-lights {
2 | z-index: 98;
3 | pointer-events: none;
4 | justify-content: center;
5 | align-items: flex-start;
6 | display: flex;
7 | position: absolute;
8 | top: 0%;
9 | bottom: auto;
10 | left: 0%;
11 | right: 0%;
12 | }
13 |
14 | .light-left {
15 | width: 80vw;
16 | max-width: 1440px;
17 | position: absolute;
18 | top: 0%;
19 | bottom: auto;
20 | left: 0%;
21 | right: auto;
22 | animation: glowRighte 8s infinite alternate ease-in-out;
23 | }
24 |
25 | .light-right {
26 | width: 80vw;
27 | max-width: 1440px;
28 | position: absolute;
29 | top: 0%;
30 | bottom: auto;
31 | left: auto;
32 | right: 0%;
33 | animation: glowRight 10s infinite alternate ease-in-out;
34 | }
35 |
36 | .light-wrap {
37 | width: 100%;
38 | min-width: 1440px;
39 | position: relative;
40 | }
41 |
42 | @keyframes glowLeft {
43 | 0% {
44 | transform: scale(0.95);
45 | opacity: 0.9;
46 | }
47 |
48 | 50% {
49 | transform: scale(1.03);
50 | opacity: 1;
51 | }
52 |
53 | 100% {
54 | transform: scale(0.98);
55 | opacity: 0.95;
56 | }
57 | }
58 |
59 | @keyframes glowRight {
60 | 0% {
61 | transform: scale(0.17);
62 | opacity: 0.63;
63 | }
64 |
65 | 60% {
66 | transform: scale(0.4);
67 | opacity: 0.73;
68 | }
69 |
70 | 100% {
71 | transform: scale(0.77);
72 | opacity: 0.83;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/styles/modules/noise.module.css:
--------------------------------------------------------------------------------
1 | .noise {
2 | z-index: 99999;
3 | opacity: 0.22;
4 | pointer-events: none;
5 | background-image: url('https: //assets-global.website-files.com/65b8e9cb3c15d9b62f057c9a/65b8fe1b1063b88db89fa90a_Noise.png');
6 | /* soDo: take local image */
7 | background-position: 0 0;
8 | background-size: 1440px;
9 | height: 100%;
10 | position: absolute;
11 | top: 0%;
12 | bottom: 0%;
13 | left: 0%;
14 | right: 0%;
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(landing)/styles/modules/noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/app/(non-dashboard)/(landing)/styles/modules/noise.png
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(miscellaneous)/changelog/layout.tsx:
--------------------------------------------------------------------------------
1 | import Particles from '@/components/effects/particles'
2 | import Container from '../../(landing)/_components/page-container'
3 |
4 | export default function RootLayout({ children }: PageProps) {
5 | return (
6 | <>
7 | {children}
8 |
9 | >
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(miscellaneous)/design-system/_components/DesignSystemWrapper.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { DesignSystemWrapperProps } from '@/types/design-system'
4 | import { Button, Card } from 'ui'
5 |
6 | export default function DesignSystemWrapper({
7 | title,
8 | description,
9 | actionButtons = [],
10 | children
11 | }: DesignSystemWrapperProps) {
12 | return (
13 |
14 |
15 |
{title}
16 |
{description}
17 |
18 | {actionButtons.length > 0 && (
19 |
20 |
21 | {actionButtons.map((button, index) => (
22 |
29 | ))}
30 |
31 |
32 | )}
33 |
34 | {children}
35 |
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(miscellaneous)/hooks-showcase/geolocation/page.tsx:
--------------------------------------------------------------------------------
1 | import { HooksShowcaseWrapper } from '../_components/hooks-showcase-wrapper'
2 | import GeolocationUI from './_components/geolocation-showcase'
3 |
4 | export default function GeolocationDemo() {
5 | return (
6 | }
14 | >
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(miscellaneous)/hooks-showcase/palette/page.tsx:
--------------------------------------------------------------------------------
1 | import { TailwindColorPalette } from '@/components/_development-utils/tailwind-color-palette'
2 |
3 | export default function page() {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(miscellaneous)/layout.tsx:
--------------------------------------------------------------------------------
1 | export default function ShowcaseLayout({
2 | children
3 | }: {
4 | children: React.ReactNode
5 | }) {
6 | return (
7 | <>
8 |
9 | {children}
10 |
11 | >
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/(miscellaneous)/old-landing/page.tsx:
--------------------------------------------------------------------------------
1 | import BentoGridIntro from '@/components/_old-landing/bent-grid'
2 | import Hero from '@/components/_old-landing/Hero/Hero'
3 |
4 | export default function Home() {
5 | return (
6 | <>
7 |
8 |
9 |
10 |
11 | >
12 | )
13 | }
14 |
15 | interface PageProps {
16 | children: React.ReactNode
17 | }
18 |
19 | function Wrapper({ children }: PageProps) {
20 | return (
21 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/docs/layout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function DocsLayout({
4 | children
5 | }: {
6 | children: React.ReactNode
7 | }) {
8 | return {children}
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/(non-dashboard)/layout.tsx:
--------------------------------------------------------------------------------
1 | import ThemeWrapper from '@/components/base/ThemeWrapper'
2 | import Footer from './(landing)/_components/footer/footer'
3 | import Header from './(landing)/_components/navigation/header'
4 |
5 | export default function RootLayout({ children }: PageProps) {
6 | return (
7 |
8 |
9 | {children}
10 |
11 |
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/api/account/route.ts:
--------------------------------------------------------------------------------
1 | import { getUserAuth } from '@/core/server/auth/utils'
2 | import { users } from '@/core/server/db/schema/auth'
3 |
4 | import { db } from 'db'
5 | import { eq } from 'drizzle-orm'
6 | import { revalidatePath } from 'next/cache'
7 |
8 | export async function PUT(request: Request) {
9 | const { session } = await getUserAuth()
10 | if (!session) return new Response('Error', { status: 400 })
11 | const body = (await request.json()) as { name?: string; email?: string }
12 |
13 | await db
14 | .update(users)
15 | .set({ ...body })
16 | .where(eq(users.id, session.user.id))
17 | revalidatePath('/account')
18 | return new Response(JSON.stringify({ message: 'ok' }), { status: 200 })
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/api/auth/logout/route.ts:
--------------------------------------------------------------------------------
1 | import { lucia } from '@/core/server/auth/lucia'
2 | import { cookies } from 'next/headers'
3 | import { NextResponse } from 'next/server'
4 |
5 | export async function POST() {
6 | const sessionId = cookies().get(lucia.sessionCookieName)?.value
7 |
8 | if (!sessionId) {
9 | return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
10 | }
11 |
12 | await lucia.invalidateSession(sessionId)
13 |
14 | const sessionCookie = lucia.createBlankSessionCookie()
15 | cookies().set(
16 | sessionCookie.name,
17 | sessionCookie.value,
18 | sessionCookie.attributes
19 | )
20 |
21 | return NextResponse.json({ success: true })
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/api/auth/session/route.ts:
--------------------------------------------------------------------------------
1 | import { getUserAuth } from '@/core/server/auth/utils'
2 |
3 | import { NextResponse } from 'next/server'
4 |
5 | export async function GET() {
6 | const { session } = await getUserAuth()
7 | return NextResponse.json({ user: session?.user || null })
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/dashboard/activity/page.tsx:
--------------------------------------------------------------------------------
1 | import ActivityFeed from '@/components/dashboard/features/activity/activity-feed'
2 |
3 | export default function ActivityPage() {
4 | return (
5 | <>
6 | Your Activity
7 |
8 | >
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/dashboard/client-wrapper.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import MainSidebar from '@/components/aside/sidebar'
4 | import SidebarSkeletonLoader from '@/components/aside/skeleton.sidebbar'
5 | import SubSidebarShell from '@/components/aside/sub-sidebar-shell'
6 | import MainContentWrapper from '@/components/base/layout/main-content-wrapper'
7 | import Navigation from '@/components/dashboard/layout/top-bar/top-bar'
8 | import { useMainSidebarStore, useSubSidebarStore } from '@/core/stores'
9 | import { ReactNode, Suspense } from 'react'
10 |
11 | type ClientWrapperProps = {
12 | children: ReactNode
13 | }
14 |
15 | export default function ClientWrapper({ children }: ClientWrapperProps) {
16 | const {
17 | isCollapsed: isMainSidebarCollapsed,
18 | toggleCollapse: toggleMainSidebar
19 | } = useMainSidebarStore()
20 | const { isOpen: isSubSidebarOpen, toggle: toggleSubSidebar } =
21 | useSubSidebarStore()
22 |
23 | return (
24 | <>
25 |
26 | }>
27 |
33 |
34 |
35 | {children}
36 | >
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/dashboard/finance/_components/display-goal.tsxpwd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/app/dashboard/finance/_components/display-goal.tsxpwd
--------------------------------------------------------------------------------
/src/app/dashboard/finance/_components/summary-card.tsx:
--------------------------------------------------------------------------------
1 | import { Card, CardContent, CardHeader, CardTitle } from 'ui'
2 | import React from 'react'
3 | import { motion } from 'framer-motion'
4 | interface SummaryCardProps {
5 | summary: {
6 | income: number
7 | expenses: number
8 | savings: number
9 | }
10 | }
11 |
12 | export default function SummaryCard({ summary }: SummaryCardProps) {
13 | return (
14 |
19 |
20 |
21 | Summary
22 |
23 |
24 |
25 |
26 | Income:
27 |
28 | €{summary.income}
29 |
30 |
31 |
32 | Expenses:
33 |
34 | €{summary.expenses}
35 |
36 |
37 |
38 | Savings:
39 |
40 | €{summary.savings}
41 |
42 |
43 |
44 |
45 |
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/dashboard/finance/layout.tsx:
--------------------------------------------------------------------------------
1 | export default function FinanceLayout({
2 | children
3 | }: {
4 | children: React.ReactNode
5 | }) {
6 | return {children}
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/dashboard/finance/page.tsx:
--------------------------------------------------------------------------------
1 | import FinanceDashboard from './_components/finance-dashboard'
2 |
3 | export default function DashboardPage() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/src/app/dashboard/folders/_hooks/use-file-operations.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/app/dashboard/folders/_hooks/use-file-operations.ts
--------------------------------------------------------------------------------
/src/app/dashboard/ig-follower-parser/page.tsx:
--------------------------------------------------------------------------------
1 | import CsvModifier from './_components/csv-ig-parser'
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 | CSV Text Modifier
8 |
9 |
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/dashboard/layout.client.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import SidebarSkeletonLoader from '@/components/aside/skeleton.sidebbar'
4 | import SubSidebarShell from '@/components/aside/sub-sidebar-shell'
5 | import MainContentWrapper from '@/components/base/layout/main-content-wrapper'
6 | import { useMainSidebarStore, useSubSidebarStore } from '@/core/stores'
7 | import { ReactNode, Suspense, lazy } from 'react'
8 | const MainSidebar = lazy(() => import('@/components/aside/sidebar'))
9 |
10 | type ClientWrapperProps = {
11 | children?: ReactNode
12 | }
13 |
14 | export default function ClientWrapper({ children }: ClientWrapperProps) {
15 | const {
16 | isCollapsed: isMainSidebarCollapsed,
17 | toggleCollapse: toggleMainSidebar
18 | } = useMainSidebarStore()
19 | const { isOpen: isSubSidebarOpen, toggle: toggleSubSidebar } =
20 | useSubSidebarStore()
21 |
22 | return (
23 | <>
24 | }>
25 |
31 |
32 |
33 | {children}
34 | >
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/app/dashboard/layout.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import MainSidebar from '@/components/aside/sidebar'
4 | import SidebarSkeletonLoader from '@/components/aside/skeleton.sidebbar'
5 | import SubSidebarShell from '@/components/aside/sub-sidebar-shell'
6 | import MainContentWrapper from '@/components/base/layout/main-content-wrapper'
7 | import Navigation from '@/components/dashboard/layout/top-bar/top-bar'
8 | import { useMainSidebarStore, useSubSidebarStore } from '@/core/stores'
9 | import { ReactNode, Suspense } from 'react'
10 |
11 | type ClientWrapperProps = {
12 | children: ReactNode
13 | userEmail: string
14 | }
15 |
16 | export default function ClientWrapper({ children }: ClientWrapperProps) {
17 | const {
18 | isCollapsed: isMainSidebarCollapsed,
19 | toggleCollapse: toggleMainSidebar
20 | } = useMainSidebarStore()
21 | const { isOpen: isSubSidebarOpen, toggle: toggleSubSidebar } =
22 | useSubSidebarStore()
23 |
24 | return (
25 | <>
26 |
27 | }>
28 |
34 |
35 |
36 | {children}
37 | >
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/app/dashboard/media-recorder/page.tsx:
--------------------------------------------------------------------------------
1 | import MediaRecorder from '@/components/features/record-peripherals/peripherals-recorder'
2 |
3 | export default function RecordingPage() {
4 | return (
5 |
6 |
7 | Media Recorder
8 |
9 |
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/dashboard/profile/get-user-profile.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { userProfiles } from '@/core/server/db/schema/auth'
4 | import { UserProfile } from '@/types/types.users'
5 | import { db } from 'db'
6 | import { eq } from 'drizzle-orm'
7 | import { revalidatePath } from 'next/cache'
8 |
9 | export async function getUserProfile(userId: string): Promise {
10 | const profile = await db.query.userProfiles.findFirst({
11 | where: eq(userProfiles.userId, userId)
12 | })
13 | return profile || {}
14 | }
15 |
16 | export async function updateUserProfile(
17 | data: UserProfile
18 | ): Promise<{ success: boolean; error?: string }> {
19 | try {
20 | const userId = data.userId
21 | if (!userId) {
22 | throw new Error('User ID is required')
23 | }
24 |
25 | await db
26 | .update(userProfiles)
27 | .set({
28 | firstName: data.firstName,
29 | lastName: data.lastName,
30 | username: data.username,
31 | dateOfBirth: data.dateOfBirth,
32 | occupation: data.occupation,
33 | bio: data.bio,
34 | github: data.github,
35 | linkedin: data.linkedin,
36 | twitter: data.twitter,
37 | updatedAt: new Date()
38 | })
39 | .where(eq(userProfiles.userId, userId))
40 |
41 | revalidatePath('/settings')
42 | return { success: true }
43 | } catch (error) {
44 | console.error('Failed to update user profile:', error)
45 | return { success: false, error: 'Failed to update user profile' }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/app/dashboard/tasks/page.tsx:
--------------------------------------------------------------------------------
1 | import TaskManagement from '@/components/dashboard/features/tasks/task-management'
2 | import { Metadata } from 'next'
3 |
4 | export const metadata: Metadata = {
5 | title: 'Task Management',
6 | description:
7 | 'Manage your tasks efficiently with our task management system.'
8 | }
9 |
10 | export default function TaskPage() {
11 | return (
12 |
13 |
14 | Task Management
15 |
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/dashboard/text-processor/id-username/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import TextProcessor from '@/components/features/text-processor/text-processor'
4 |
5 | const extractIdAndUsername = (
6 | input: string,
7 | options: Record
8 | ): string => {
9 | const lines = input.split('\n')
10 | return lines
11 | .map(line => {
12 | const idMatch = line.match(/id[:=]?\s*"?(\d+)"?/i)
13 | const usernameMatch = line.match(
14 | /(?:user|name)[:=]?\s*"?([^"\n]+)"?/i
15 | )
16 |
17 | let result = ''
18 | if (options.keepId && idMatch) {
19 | result += `ID: ${idMatch[1]}`
20 | }
21 | if (options.keepUsername && usernameMatch) {
22 | result += (result ? ', ' : '') + `Username: ${usernameMatch[1]}`
23 | }
24 | return result || line
25 | })
26 | .filter(line => line.trim() !== '')
27 | .join('\n')
28 | }
29 |
30 | const processorOptions = [
31 | { id: 'keepId', label: 'Keep ID' },
32 | { id: 'keepUsername', label: 'Keep Username' }
33 | ]
34 |
35 | export default function IDUsernameProcessorPage() {
36 | return (
37 |
38 |
39 | ID and Username Extractor
40 |
41 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/dashboard/text-processor/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import TextProcessor from '@/components/features/text-processor/text-processor'
4 |
5 | const removeNumbers = (input: string): string => {
6 | return input
7 | .split('\n')
8 | .filter(line => !/\d/.test(line))
9 | .join('\n')
10 | }
11 |
12 | export default function IGParserPage() {
13 | return (
14 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import ThemeWrapper from '@/components/base/ThemeWrapper'
2 | import { metadata as rootMetadata } from '@/core/config/metadata/metadata.root-layout'
3 | import { Metadata } from 'next'
4 | import '../styles/app.scss'
5 |
6 | export const metadata: Metadata = rootMetadata
7 |
8 | export default function RootLayout({
9 | children
10 | }: {
11 | children: React.ReactNode
12 | }) {
13 | return (
14 |
20 |
21 | {children}
22 |
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/loading.tsx:
--------------------------------------------------------------------------------
1 | import Center from '@/components/atoms/Center'
2 | import HeartbeatLoader from '@/components/effects/loaders/heartbeat-loader'
3 |
4 | export default function Loading() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/opengraph-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/app/opengraph-image.png
--------------------------------------------------------------------------------
/src/app/robots.ts:
--------------------------------------------------------------------------------
1 | import { MetadataRoute } from 'next'
2 |
3 | export default function robots(): MetadataRoute.Robots {
4 | return {
5 | rules: {
6 | userAgent: '*',
7 | allow: '/',
8 | disallow: ''
9 | },
10 | sitemap: 'https://topbar.remcostoeten.com/sitemap.xml'
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/sitemap.ts:
--------------------------------------------------------------------------------
1 | import { MetadataRoute } from 'next'
2 |
3 | export default function sitemap(): MetadataRoute.Sitemap {
4 | return [
5 | {
6 | url: 'https://notevault.remcostoeten.com',
7 | lastModified: new Date(),
8 | changeFrequency: 'weekly',
9 | priority: 1
10 | },
11 | {
12 | url: 'https://notevault.remcostoeten.com/sign-up',
13 | lastModified: new Date(),
14 | changeFrequency: 'yearly',
15 | priority: 0.8
16 | },
17 | {
18 | url: 'https://notevault.remcostoeten.com/changelog',
19 | lastModified: new Date(),
20 | changeFrequency: 'daily',
21 | priority: 0.9
22 | },
23 | {
24 | url: 'https://notevault.remcostoeten.com/sign-in',
25 | lastModified: new Date(),
26 | changeFrequency: 'yearly',
27 | priority: 0.8
28 | },
29 |
30 | {
31 | url: 'https://notevault.remcostoeten.com/docs/todo',
32 | lastModified: new Date(),
33 | changeFrequency: 'monthly',
34 | priority: 0.6
35 | },
36 | {
37 | url: 'https://notevault.remcostoeten.com/design-system/notice',
38 | lastModified: new Date(),
39 | changeFrequency: 'monthly',
40 | priority: 0.6
41 | },
42 | {
43 | url: 'https://notevault.remcostoeten.com/hooks-showcase/geolocation',
44 | lastModified: new Date(),
45 | changeFrequency: 'monthly',
46 | priority: 0.6
47 | },
48 | {
49 | url: 'https://notevault.remcostoeten.com/docs/todo',
50 | lastModified: new Date(),
51 | changeFrequency: 'monthly',
52 | priority: 0.6
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/Banner.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useState, useEffect } from 'react'
4 | import { useLocalStorage } from '@/core/hooks/use-local-storage'
5 |
6 | type BannerProps = {
7 | // Add any props you need for the banner
8 | }
9 |
10 | export default function Banner({}: BannerProps) {
11 | const [isVisible, setIsVisible] = useState(false)
12 | const [, , , setDismissed, isDismissed] = useLocalStorage('banner', false)
13 |
14 | useEffect(() => {
15 | setIsVisible(!isDismissed())
16 | }, [isDismissed])
17 |
18 | const handleDismiss = () => {
19 | setIsVisible(false)
20 | setDismissed()
21 | }
22 |
23 | if (!isVisible) return null
24 |
25 | return (
26 |
27 |
28 |
Your banner message here
29 |
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import { ThemeProvider as NextThemesProvider } from 'next-themes'
5 | import { type ThemeProviderProps } from 'next-themes/dist/types'
6 |
7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8 | return {children}
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/_development-utils/auth-status-indicator.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import {
4 | ClientAuthSession,
5 | useClientAuth
6 | } from '@/core/server/auth/client-auth-utils'
7 | import { Frown, Smile } from 'lucide-react'
8 | import { useEffect, useState } from 'react'
9 | import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
10 |
11 | export default function AuthStatusIndicator() {
12 | const [session, setSession] = useState(null)
13 | const { getClientSession } = useClientAuth()
14 |
15 | useEffect(() => {
16 | const fetchSession = async () => {
17 | const { user } = await getClientSession()
18 | setSession(user)
19 | }
20 | fetchSession()
21 | }, [getClientSession])
22 |
23 | return (
24 |
31 |
32 |
33 | {session ? (
34 |
35 | ) : (
36 |
37 | )}
38 |
39 |
40 | {session ? 'Authenticated' : 'Not Authenticated'}
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/_old-landing/Hero/Gradual-spacing.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { cn } from 'cn'
3 | import { AnimatePresence, Variants, motion } from 'framer-motion'
4 | import React from 'react'
5 |
6 | interface GradualSpacingProps {
7 | text: string
8 | duration?: number
9 | delayMultiple?: number
10 | framerProps?: Variants
11 | className?: string
12 | textClassName?: string
13 | visiblity?: boolean
14 | breakAfter?: string
15 | }
16 |
17 | export default function GradualSpacing({
18 | text,
19 | textClassName = 'justify-start items-center',
20 | duration = 0.5,
21 | delayMultiple = 0.04,
22 | framerProps = {
23 | hidden: { opacity: 0, x: -20 },
24 | visible: { opacity: 1, x: 0 }
25 | },
26 | className,
27 | visiblity,
28 | breakAfter
29 | }: GradualSpacingProps) {
30 | const words = text.split(' ')
31 | return (
32 |
33 |
34 | {words.map((word, i) => {
35 | const shouldBreak =
36 | breakAfter &&
37 | words
38 | .slice(0, i + 1)
39 | .join(' ')
40 | .endsWith(breakAfter)
41 | return (
42 |
43 |
57 | {word}
58 |
59 | {shouldBreak &&
}
60 |
61 | )
62 | })}
63 |
64 |
65 | )
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/_old-landing/Hero/Maq.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 | import React from 'react'
3 |
4 | interface MarqueeProps {
5 | className?: string
6 | reverse?: boolean
7 | pauseOnHover?: boolean
8 | children?: React.ReactNode
9 | vertical?: boolean
10 | repeat?: number
11 | [key: string]: any
12 | }
13 |
14 | export default function Marquee({
15 | className,
16 | reverse,
17 | pauseOnHover = false,
18 | children,
19 | vertical = false,
20 | repeat = 4,
21 | ...props
22 | }: MarqueeProps) {
23 | return (
24 |
35 | {Array(repeat)
36 | .fill(0)
37 | .map((_, i) => (
38 |
51 | {children}
52 |
53 | ))}
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/_old-landing/footer.tsx:
--------------------------------------------------------------------------------
1 | import { TextHoverEffect } from '@/components/effects/glow-text-effect'
2 | export function Footer() {
3 | return (
4 |
5 |
6 |
7 | )
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/aside/skeleton.sidebbar.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | type SidebarSkeletonLoaderProps = {
4 | width?: string
5 | height?: string
6 | }
7 |
8 | export default function SidebarSkeletonLoader({
9 | width = '250px',
10 | height = '100vh'
11 | }: SidebarSkeletonLoaderProps) {
12 | return (
13 |
22 |
26 |
30 |
34 |
38 |
42 |
46 |
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/aside/skeleton.sub-sidebar.tsx:
--------------------------------------------------------------------------------
1 | import { Skeleton } from 'ui'
2 | export default function SubSidebarSkeletonLoader() {
3 | return (
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/aside/types.sidear.d.ts:
--------------------------------------------------------------------------------
1 | export type SidebarIconProps = {
2 | item: SidebarItem
3 | isActive: boolean
4 | onClick?: () => void
5 | }
6 |
7 | export type MainSidebarProps = {
8 | isSubSidebarOpen: boolean
9 | toggleSubSidebar: () => void
10 | isCollapsed: boolean
11 | toggleCollapse: () => void
12 | }
13 |
14 | export type SiteSettingsMenuProps = {
15 | isOpen: boolean
16 | onClose: () => void
17 | onSettingChange: (setting: string, value: boolean) => void
18 | }
19 |
20 | export type SubSidebarShellProps = {
21 | isSubSidebarOpen: boolean
22 | // isMainSidebarCollapsed: boolean
23 | }
24 |
25 | export type FolderType = {
26 | id: string
27 | name: string
28 | description: string | null
29 | color: string
30 | parentId?: string | null
31 | isSelectable?: boolean
32 | }
33 |
34 | export type DropdownAction = {
35 | label: string
36 | icon: React.ReactNode
37 | onClick: () => void
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/atoms/Center.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 |
3 | type CenterProps = {
4 | children: ReactNode
5 | method?: 'grid' | 'absolute' | 'flex'
6 | direction?: 'vertical' | 'horizontal' | 'both'
7 | className?: string
8 | }
9 |
10 | export default function Center({
11 | children,
12 | method = 'flex',
13 | direction = 'both',
14 | className = ''
15 | }: CenterProps) {
16 | let centerStyles = ''
17 |
18 | switch (method) {
19 | case 'grid':
20 | centerStyles = 'grid w-screen h-screen place-items-center'
21 | break
22 | case 'absolute':
23 | centerStyles = 'absolute'
24 | if (direction === 'vertical' || direction === 'both') {
25 | centerStyles += ' top-1/2 -translate-y-1/2'
26 | }
27 | if (direction === 'horizontal' || direction === 'both') {
28 | centerStyles += ' left-1/2 -translate-x-1/2'
29 | }
30 | break
31 | case 'flex':
32 | default:
33 | centerStyles = 'flex'
34 | if (direction === 'vertical' || direction === 'both') {
35 | centerStyles += ' items-center'
36 | }
37 | if (direction === 'horizontal' || direction === 'both') {
38 | centerStyles += ' justify-center'
39 | }
40 | break
41 | }
42 |
43 | return {children}
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/atoms/Spacer.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx'
2 | import React from 'react'
3 |
4 | type SpacerProps = {
5 | children: React.ReactNode
6 | spaceX?: string
7 | spaceY?: string
8 | margin?: string
9 | padding?: string
10 | className?: string
11 | as?: keyof JSX.IntrinsicElements
12 | }
13 |
14 | /**
15 | * Spacer component to handle space-x, space-y, margin, and padding utility classes.
16 | *
17 | * @param {React.ReactNode} children - The content to be displayed inside the container.
18 | * @param {string} [spaceX] - Horizontal spacing between children (TailwindCSS space-x-* classes).
19 | * @param {string} [spaceY] - Vertical spacing between children (TailwindCSS space-y-* classes).
20 | * @param {string} [margin] - Custom margin (TailwindCSS margin classes like 'm-4', 'mt-2', etc.).
21 | * @param {string} [padding] - Custom padding (TailwindCSS padding classes like 'p-4', 'pt-2', etc.).
22 | * @param {string} [className=''] - Additional custom class names.
23 | * @param {keyof JSX.IntrinsicElements} [as='div'] - The HTML element to render as.
24 | * @returns {JSX.Element} The rendered Spacer component.
25 | */
26 |
27 | export function Spacer({
28 | children,
29 | spaceX = '0',
30 | spaceY = '0',
31 | margin = '0',
32 | padding = '0',
33 | className = '',
34 | as: Component = 'div',
35 | ...props
36 | }: SpacerProps) {
37 | const classes = clsx(
38 | {
39 | [`space-x-${spaceX}`]: spaceX !== '0',
40 | [`space-y-${spaceY}`]: spaceY !== '0',
41 | [`m-${margin}`]: margin !== '0',
42 | [`p-${padding}`]: padding !== '0'
43 | },
44 | className
45 | )
46 |
47 | return (
48 |
49 | {children}
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/atoms/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Center'
2 | export * from './Flex'
3 | export * from './@@Kbd'
4 | export * from './Spacer'
5 |
--------------------------------------------------------------------------------
/src/components/auth/auth.d.ts:
--------------------------------------------------------------------------------
1 | import { updateUserProfileSchema } from '@/core/server/db/schema/auth'
2 | import { z } from 'zod'
3 |
4 | export type WithUserInfoProps = {
5 | userInfo: ReturnType
6 | enhancedSubmit: (
7 | event: React.FormEvent,
8 | originalSubmit: (formData: FormData) => Promise
9 | ) => Promise
10 | }
11 |
12 | export type UpdateUserProfileInput = z.infer
13 |
14 | export type OnboardingProps = {
15 | userId: string
16 | }
17 |
18 | export type SignInFormProps = {
19 | userInfo: {
20 | device: string
21 | location: string
22 | timezone: string
23 | lastPage: string
24 | os: string
25 | }
26 | enhancedSubmit: (
27 | event: React.FormEvent,
28 | originalSubmit: (formData: FormData) => Promise
29 | ) => Promise
30 | }
31 |
32 | export type SignUpFormProps = {
33 | userInfo: {
34 | device: string
35 | location: string
36 | timezone: string
37 | lastPage: string
38 | os: string
39 | }
40 | enhancedSubmit: (
41 | event: React.FormEvent,
42 | originalSubmit: (formData: FormData) => Promise
43 | ) => Promise
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/auth/sign-out-button.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { signOutAction } from '@/core/server/actions/user/action.sign-out'
4 | import { LogOut } from 'lucide-react'
5 | import { useRouter } from 'next/navigation'
6 | import { useState } from 'react'
7 | import { toast } from 'sonner'
8 | import { Button } from 'ui'
9 |
10 | export default function SignOutBtn() {
11 | const router = useRouter()
12 | const [isSigningOut, setIsSigningOut] = useState(false)
13 |
14 | const handleSignOut = async () => {
15 | setIsSigningOut(true)
16 | try {
17 | const formData = new FormData()
18 | formData.append('device', navigator.userAgent)
19 | formData.append('location', window.location.href)
20 | formData.append(
21 | 'timezone',
22 | Intl.DateTimeFormat().resolvedOptions().timeZone
23 | )
24 | formData.append('lastPage', window.location.pathname)
25 | formData.append('os', navigator.platform)
26 | toast('Signing out...')
27 | try {
28 | await signOutAction(formData)
29 | router.push('/sign-in')
30 | router.refresh()
31 | toast.success('Signed out successfully')
32 | } catch (error) {
33 | toast.error('Sign out failed')
34 | console.error('Sign out failed:', error)
35 | }
36 | } catch (error) {
37 | console.error('Sign out failed:', error)
38 | } finally {
39 | setIsSigningOut(false)
40 | }
41 | }
42 |
43 | return (
44 |
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/auth/user-dropdown-menu/user-dropdown-menu-item.tsx:
--------------------------------------------------------------------------------
1 | type MenuItemProps = {
2 | label: string
3 | link?: string
4 | onClick?: () => void
5 | }
6 |
7 | function siMenuItem({ label, link, onClick }: MenuItemProps) {
8 | const baseClasses =
9 | 'flex-1 shrink self-stretch pt-3.5 pr-4 pb-3 pl-4 w-full whitespace-nowrap rounded-lg hover:bg-zinc-900 transition duration-200 ease-in-out'
10 | const textColor = 'dark:text-title text-black'
11 | const cursorPointer = link || onClick ? 'cursor-pointer' : ''
12 |
13 | if (link) {
14 | return (
15 |
16 |
17 | {label}
18 |
19 |
20 | )
21 | }
22 |
23 | return (
24 |
28 | {label}
29 |
30 | )
31 | }
32 |
33 | export default siMenuItem
34 |
--------------------------------------------------------------------------------
/src/components/base/ThemeWrapper.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { metadata } from '@/core/config/metadata/metadata.root-layout'
4 | import { Analytics } from '@vercel/analytics/react'
5 | import { SpeedInsights } from '@vercel/speed-insights/next'
6 | import NextTopLoader from 'nextjs-toploader'
7 | import posthog from 'posthog-js'
8 | import { PostHogProvider } from 'posthog-js/react'
9 | import { useEffect } from 'react'
10 | import { Toaster, TooltipProvider } from 'ui'
11 | import { ThemeProvider } from '../ThemeProvider'
12 |
13 | export { metadata }
14 |
15 | export default function ThemeWrapper({
16 | children
17 | }: Readonly<{
18 | children: React.ReactNode
19 | }>) {
20 | useEffect(() => {
21 | posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY as string, {
22 | api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
23 | person_profiles: 'identified_only',
24 | capture_pageview: false // Disable automatic pageview capture, as we capture manually
25 | })
26 | }, [])
27 |
28 | return (
29 |
30 |
36 |
37 |
45 |
46 | {children}
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/dashboard/features/activity/activities.d.ts:
--------------------------------------------------------------------------------
1 | export interface Activity {
2 | id: string
3 | color: string
4 | name: string
5 | icon: IconNames
6 | duration: number
7 | }
8 |
9 | export interface ActivityCardProps extends Activity {}
10 |
11 | export interface ActivityCardSkeletonProps {
12 | animate?: boolean
13 | opacity?: number
14 | }
15 |
16 | export interface ActivitiesProps {
17 | activities: Activity[]
18 | }
19 |
20 | export interface CreateActivityInput {
21 | name: string
22 | duration: number
23 | icon: string
24 | color: string
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/dashboard/features/activity/activity-list.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | type Activity = {
4 | id: string
5 | type: string
6 | description: string
7 | createdAt: string
8 | }
9 |
10 | interface ActivityListProps {
11 | activities: Activity[]
12 | onDelete: (id: string) => void
13 | }
14 |
15 | export const ActivityList: React.FC = ({
16 | activities,
17 | onDelete
18 | }) => {
19 | return (
20 |
21 | {activities.map(activity => (
22 | -
23 |
24 |
25 |
{activity.type}
26 |
{activity.description}
27 |
28 | {new Date(activity.createdAt).toLocaleString()}
29 |
30 |
31 |
37 |
38 |
39 | ))}
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/dashboard/features/activity/create-activity-popover.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { PlusIcon } from 'lucide-react'
4 | import { useState } from 'react'
5 | import { Button } from 'ui'
6 | import { CreateActivityForm } from './create-activity-form'
7 | import { RecentActivities } from './recent-activities'
8 |
9 | export default function ActivitiesFeature() {
10 | const [isCreateFormOpen, setIsCreateFormOpen] = useState(false)
11 |
12 | return (
13 |
14 |
15 |
16 | Activities
17 |
18 |
26 |
27 |
28 | {isCreateFormOpen &&
}
29 |
30 |
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/dashboard/features/activity/mock-activity-api.ts:
--------------------------------------------------------------------------------
1 | // mockActivityApi.ts
2 | import { Activity, CreateActivityInput } from './activities.d'
3 |
4 | const STORAGE_KEY = 'activities'
5 |
6 | export const mockActivityApi = {
7 | create: async (activity: CreateActivityInput): Promise => {
8 | return new Promise(resolve => {
9 | setTimeout(() => {
10 | const activities = JSON.parse(
11 | localStorage.getItem(STORAGE_KEY) || '[]'
12 | ) as Activity[]
13 | const newActivity: Activity = {
14 | ...activity,
15 | id: Math.random().toString(36).substr(2, 9),
16 | createdAt: new Date().toISOString()
17 | }
18 | activities.push(newActivity)
19 | localStorage.setItem(STORAGE_KEY, JSON.stringify(activities))
20 | resolve(newActivity)
21 | }, 500)
22 | })
23 | },
24 |
25 | getUserActivities: async (): Promise => {
26 | return new Promise(resolve => {
27 | setTimeout(() => {
28 | const activities = JSON.parse(
29 | localStorage.getItem(STORAGE_KEY) || '[]'
30 | ) as Activity[]
31 | resolve(activities)
32 | }, 500)
33 | })
34 | },
35 |
36 | // Add this method to clear all activities (for testing purposes)
37 | clearAllActivities: async (): Promise => {
38 | return new Promise(resolve => {
39 | setTimeout(() => {
40 | localStorage.removeItem(STORAGE_KEY)
41 | resolve()
42 | }, 500)
43 | })
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/dashboard/features/activity/use-activities.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { IconNames } from 'ui'
3 |
4 | export interface Activity {
5 | id: string
6 | name: string
7 | duration: number
8 | icon: IconNames | 'default'
9 | color: string
10 | }
11 |
12 | export function useActivities() {
13 | const [activities, setActivities] = useState([])
14 |
15 | useEffect(() => {
16 | const storedActivities = localStorage.getItem('activities')
17 | if (storedActivities) {
18 | setActivities(JSON.parse(storedActivities))
19 | }
20 | }, [])
21 |
22 | const createActivity = (activity: Omit) => {
23 | const newActivity = { ...activity, id: Date.now().toString() }
24 | const updatedActivities = [...activities, newActivity]
25 | localStorage.setItem('activities', JSON.stringify(updatedActivities))
26 | setActivities(updatedActivities)
27 | }
28 |
29 | return { activities, createActivity }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/dashboard/features/intro/current-time.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import React, { useEffect, useState } from 'react'
4 |
5 | type CurrentTimeProps = {
6 | format?: 'HH:MM' | 'full'
7 | showPeriod?: boolean
8 | }
9 |
10 | function CurrentTime({
11 | format = 'HH:MM',
12 | showPeriod = false
13 | }: CurrentTimeProps) {
14 | const [time, setTime] = useState(new Date())
15 | const [blink, setBlink] = useState(true)
16 |
17 | useEffect(() => {
18 | const timer = setInterval(() => {
19 | setTime(new Date())
20 | setBlink(prev => !prev)
21 | }, 1000)
22 | return () => clearInterval(timer)
23 | }, [])
24 |
25 | const timeOptions: Intl.DateTimeFormatOptions = {
26 | hour: '2-digit',
27 | minute: '2-digit',
28 | hour12: false
29 | }
30 |
31 | if (format !== 'HH:MM') {
32 | timeOptions.second = '2-digit'
33 | if (showPeriod) {
34 | timeOptions.hour12 = true
35 | }
36 | }
37 |
38 | const timeString = time.toLocaleTimeString([], timeOptions)
39 | const parts = timeString.split(':')
40 | const formattedTime = `${parts[0]}:${parts.slice(1).join(':')}`
41 |
42 | return (
43 |
49 | )
50 | }
51 |
52 | export default CurrentTime
53 |
--------------------------------------------------------------------------------
/src/components/dashboard/onboarding-trigger.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import OnboardingForm from '@/components/auth/onboarding'
4 | import { Button } from '@/components/ui'
5 | import { fadeInUp } from '@/core/constants/animations'
6 | import { AnimatePresence, motion } from 'framer-motion'
7 | import { useState } from 'react'
8 |
9 | type OnboardingTriggerProps = {
10 | userId: string
11 | hasCompletedOnboarding: boolean
12 | }
13 |
14 | export default function OnboardingTrigger({
15 | userId,
16 | hasCompletedOnboarding
17 | }: OnboardingTriggerProps) {
18 | const [showOnboarding, setShowOnboarding] = useState(false)
19 |
20 | const handleComplete = () => {
21 | setShowOnboarding(false)
22 | // You might want to refresh the page or update the UI to reflect the completed onboarding
23 | }
24 |
25 | if (hasCompletedOnboarding) {
26 | return null
27 | }
28 |
29 | return (
30 |
31 | {!showOnboarding ? (
32 |
40 |
43 |
44 | ) : (
45 |
53 |
57 |
58 | )}
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/effects/blur-in.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { motion } from 'framer-motion'
4 |
5 | import { cn } from 'cn'
6 |
7 | interface BlurIntProps {
8 | word: React.ReactNode
9 | className?: string
10 | variant?: {
11 | hidden: { filter: string; opacity: number }
12 | visible: { filter: string; opacity: number }
13 | }
14 | duration?: number
15 | }
16 | const BlurIn = ({ word, className, variant, duration = 1 }: BlurIntProps) => {
17 | const defaultVariants = {
18 | hidden: { filter: 'blur(10px)', opacity: 0.4 },
19 | visible: { filter: 'blur(0px)', opacity: 1 }
20 | }
21 | const combinedVariants = variant || defaultVariants
22 |
23 | return (
24 |
31 | {word}
32 |
33 | )
34 | }
35 |
36 | export default BlurIn
37 |
--------------------------------------------------------------------------------
/src/components/effects/card-spotlight/use-mouse-position.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 |
3 | interface MousePosition {
4 | x: number
5 | y: number
6 | }
7 |
8 | export default function useMousePosition(): MousePosition {
9 | const [mousePosition, setMousePosition] = useState({
10 | x: 0,
11 | y: 0
12 | })
13 |
14 | useEffect(() => {
15 | const handleMouseMove = (event: MouseEvent) => {
16 | setMousePosition({ x: event.clientX, y: event.clientY })
17 | }
18 |
19 | window.addEventListener('mousemove', handleMouseMove)
20 |
21 | return () => {
22 | window.removeEventListener('mousemove', handleMouseMove)
23 | }
24 | }, [])
25 |
26 | return mousePosition
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/effects/card-spotlight/useMousePosition.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 |
3 | interface MousePosition {
4 | x: number
5 | y: number
6 | }
7 |
8 | export default function useMousePosition(): MousePosition {
9 | const [mousePosition, setMousePosition] = useState({
10 | x: 0,
11 | y: 0
12 | })
13 |
14 | useEffect(() => {
15 | const handleMouseMove = (event: MouseEvent) => {
16 | setMousePosition({ x: event.clientX, y: event.clientY })
17 | }
18 |
19 | window.addEventListener('mousemove', handleMouseMove)
20 |
21 | return () => {
22 | window.removeEventListener('mousemove', handleMouseMove)
23 | }
24 | }, [])
25 |
26 | return mousePosition
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/effects/fade-in.tsx:
--------------------------------------------------------------------------------
1 | import { motion } from 'framer-motion'
2 | import React from 'react'
3 |
4 | type FadeInProps = {
5 | children: React.ReactNode
6 | delay?: number
7 | duration?: number
8 | }
9 |
10 | const FadeIn: React.FC = ({
11 | children,
12 | delay = 0,
13 | duration = 0.5
14 | }) => {
15 | return (
16 |
21 | {children}
22 |
23 | )
24 | }
25 |
26 | export default FadeIn
27 |
--------------------------------------------------------------------------------
/src/components/effects/loaders/heartbeat-loader.tsx:
--------------------------------------------------------------------------------
1 | export default function HeartbeatLoader() {
2 | return (
3 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/effects/loaders/loading-dots.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | type LoadingDotsProps = {
4 | size?: 1 | 2 | 3 | 4 | 5
5 | color?: string
6 | opacity?: number
7 | className?: string
8 | } & React.HTMLAttributes
9 |
10 | export default function LoadingDots({
11 | size = 4,
12 | color = 'bg-gray-900',
13 | opacity = 0.2,
14 | className = '',
15 | ...props
16 | }: LoadingDotsProps) {
17 | const dotSize = `${size}px`
18 |
19 | return (
20 |
27 | {[0, 1, 2].map(dot => (
28 |
39 | ))}
40 |
53 |
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/effects/loaders/skeleton-loader.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 | import React from 'react'
3 |
4 | export interface SkeletonProps extends React.HTMLAttributes {
5 | count?: number
6 | className?: string
7 | as?: React.ElementType
8 | }
9 |
10 | export function Skeleton({ className, ...props }: SkeletonProps) {
11 | return (
12 |
16 | )
17 | }
18 |
19 | export default function SkeletonLoader({
20 | count = 1,
21 | className,
22 | as: Component = 'div',
23 | ...props
24 | }: SkeletonProps) {
25 | return (
26 | <>
27 | {Array(count)
28 | .fill(null)
29 | .map((_, index) => (
30 |
31 |
32 |
33 | ))}
34 | >
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/effects/loaders/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Skeleton = ({ className }: { className: string }) => (
4 |
5 |
6 |
7 |
8 |
9 |
10 | )
11 |
12 | const SVGSkeleton = ({ className }: { className: string }) => (
13 |
14 | )
15 |
16 | export { Skeleton, SVGSkeleton }
17 |
--------------------------------------------------------------------------------
/src/components/effects/number-ticker.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useInView, useMotionValue, useSpring } from 'framer-motion'
4 | import { useEffect, useRef } from 'react'
5 |
6 | import { cn } from 'cn'
7 |
8 | export default function NumberTicker({
9 | value,
10 | direction = 'up',
11 | delay = 0,
12 | className
13 | }: {
14 | value: number
15 | direction?: 'up' | 'down'
16 | className?: string
17 | delay?: number // delay in s
18 | }) {
19 | const ref = useRef(null)
20 | const motionValue = useMotionValue(direction === 'down' ? value : 0)
21 | const springValue = useSpring(motionValue, {
22 | damping: 60,
23 | stiffness: 100
24 | })
25 | const isInView = useInView(ref, { once: true, margin: '0px' })
26 |
27 | useEffect(() => {
28 | isInView &&
29 | setTimeout(() => {
30 | motionValue.set(direction === 'down' ? 0 : value)
31 | }, delay * 1000)
32 | }, [motionValue, isInView, delay, value, direction])
33 |
34 | useEffect(
35 | () =>
36 | springValue.on('change', latest => {
37 | if (ref.current) {
38 | ref.current.textContent = Intl.NumberFormat('en-US').format(
39 | Number(latest.toFixed(0))
40 | )
41 | }
42 | }),
43 | [springValue]
44 | )
45 |
46 | return (
47 |
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/effects/ripple.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 | import React, { CSSProperties } from 'react'
3 |
4 | interface RippleProps {
5 | mainCircleSize?: number
6 | mainCircleOpacity?: number
7 | numCircles?: number
8 | className?: string
9 | }
10 |
11 | const Ripple = React.memo(function Ripple({
12 | mainCircleSize = 210,
13 | mainCircleOpacity = 0.24,
14 | numCircles = 8,
15 | className
16 | }: RippleProps) {
17 | return (
18 |
24 | {Array.from({ length: numCircles }, (_, i) => {
25 | const size = mainCircleSize + i * 70
26 | const opacity = mainCircleOpacity - i * 0.03
27 | const animationDelay = `${i * 0.06}s`
28 | const borderStyle = i === numCircles - 1 ? 'dashed' : 'solid'
29 | const borderOpacity = 5 + i * 5
30 |
31 | return (
32 |
50 | )
51 | })}
52 |
53 | )
54 | })
55 |
56 | Ripple.displayName = 'Ripple'
57 |
58 | export default Ripple
59 |
--------------------------------------------------------------------------------
/src/components/effects/show-hide.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useState } from 'react'
4 | import { Button } from 'ui'
5 | import { AnimateHeight } from '../dashboard/features/activity/animate-height'
6 |
7 | type ShowHideProps = {
8 | title: string
9 | children: React.ReactNode
10 | width?: string
11 | className?: string
12 | }
13 |
14 | export default function ShowHide({
15 | title,
16 | children,
17 | width = 'w-full',
18 | className = '',
19 | ...props
20 | }: ShowHideProps) {
21 | const [isExpanded, setIsExpanded] = useState(true)
22 |
23 | return (
24 |
28 |
29 |
30 | {title}
31 |
32 |
42 |
43 |
44 |
45 | {children}
46 |
47 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/elements/crud/add-note-modal.tsx:
--------------------------------------------------------------------------------
1 | import RichTextEditor from '@/components/rich-text-editor'
2 | import { useState } from 'react'
3 | import {
4 | Button,
5 | Dialog,
6 | DialogContent,
7 | DialogHeader,
8 | DialogTitle,
9 | Input
10 | } from 'ui'
11 |
12 | type NewNoteModalProps = {
13 | isOpen: boolean
14 | onClose: () => void
15 | onCreateNote: (title: string, content: string) => void
16 | }
17 |
18 | export default function NewNoteModal({
19 | isOpen,
20 | onClose,
21 | onCreateNote
22 | }: NewNoteModalProps) {
23 | const [newNoteTitle, setNewNoteTitle] = useState('')
24 | const [newNoteContent, setNewNoteContent] = useState('')
25 |
26 | const handleCreateNote = () => {
27 | onCreateNote(newNoteTitle, newNoteContent)
28 | setNewNoteTitle('')
29 | setNewNoteContent('')
30 | }
31 |
32 | return (
33 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/elements/crud/confirmation-modal.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Dialog, DialogContent, DialogHeader, DialogTitle } from 'ui'
2 | import React from 'react'
3 |
4 | type ConfirmationModalProps = {
5 | isOpen: boolean
6 | onClose: () => void
7 | onConfirm: () => void
8 | title?: string
9 | message?: string
10 | confirmText?: string
11 | cancelText?: string
12 | icon?: React.ReactNode
13 | }
14 |
15 | export default function ConfirmationModal({
16 | isOpen,
17 | onClose,
18 | onConfirm,
19 | title = 'Confirm Action',
20 | message = 'Are you sure you want to perform this action?',
21 | confirmText = 'Confirm',
22 | cancelText = 'Cancel',
23 | icon
24 | }: ConfirmationModalProps) {
25 | return (
26 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/elements/display-code/code-highlight/CodeContent.tsx:
--------------------------------------------------------------------------------
1 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
2 | import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
3 |
4 | interface CodeContentProps {
5 | children: string
6 | language: string
7 | }
8 |
9 | const CodeContent: React.FC = ({ children, language }) => {
10 | return (
11 |
25 | {children}
26 |
27 | )
28 | }
29 |
30 | export default CodeContent
31 |
--------------------------------------------------------------------------------
/src/components/elements/display-code/code-highlight/FileHeader.tsx:
--------------------------------------------------------------------------------
1 | type FileHeaderProps = {
2 | title?: string
3 | onCopy: () => void
4 | disableMinWidth: boolean
5 | allowCollapse: boolean
6 | isCollapsed: boolean
7 | onToggleCollapse: () => void
8 | showModal: boolean
9 | modalTrigger: React.ReactNode
10 | }
11 |
12 | const FileHeader: React.FC = ({
13 | title,
14 | onCopy,
15 | disableMinWidth,
16 | allowCollapse,
17 | isCollapsed,
18 | onToggleCollapse,
19 | showModal,
20 | modalTrigger
21 | }) => {
22 | return (
23 |
24 |
25 | {title}
26 | {showModal && modalTrigger}
27 |
28 |
29 |
35 | {allowCollapse && (
36 |
42 | )}
43 |
44 |
45 | )
46 | }
47 |
48 | export default FileHeader
49 |
--------------------------------------------------------------------------------
/src/components/elements/display-code/code-highlight/types.code-highlight.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 |
3 | export type CodeContentProps = {
4 | children: ReactNode
5 | language?:
6 | | 'language'
7 | | 'jsx'
8 | | 'tsx'
9 | | 'javascript'
10 | | 'typescript'
11 | | 'json'
12 | | 'bash'
13 | | 'shell'
14 | | 'python'
15 | | 'java'
16 | | 'csharp'
17 | | 'cpp'
18 | | 'css'
19 | | 'scss'
20 | | 'sass'
21 | }
22 |
23 | export type CodeHighlightProps = {
24 | title: string
25 | fileIcon?: string
26 | avatarSrc?: string
27 | language?: string
28 | children?: React.ReactNode
29 | disableMinWidth?: boolean
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/elements/index.ts:
--------------------------------------------------------------------------------
1 | export * from './display-code/code-highlight/code-highlight'
2 | export * from './custom-dropdown'
3 | export * from './notice-box'
4 |
--------------------------------------------------------------------------------
/src/components/features/record-peripherals/peripherals-recorder.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useState } from 'react'
4 | import { Tabs, TabsContent, TabsList, TabsTrigger } from 'ui'
5 | import AudioRecorder from './audio-recorder'
6 | import WebcamRecorder from './display-webcam'
7 |
8 | export default function MediaRecorder() {
9 | const [activeTab, setActiveTab] = useState('Audio')
10 |
11 | return (
12 |
17 |
18 | Audio
19 | Webcam
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/tiptap-editor.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { EditorContent, useEditor } from '@tiptap/react'
4 | import StarterKit from '@tiptap/starter-kit'
5 |
6 | export default function TipTapEditor({
7 | content,
8 | onChange
9 | }: {
10 | content: string
11 | onChange: (html: string) => void
12 | }) {
13 | const editor = useEditor({
14 | extensions: [StarterKit],
15 | content,
16 | onUpdate: ({ editor }) => {
17 | onChange(editor.getHTML())
18 | }
19 | })
20 |
21 | return (
22 |
23 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/ui/animations.ts:
--------------------------------------------------------------------------------
1 | export const containerVariants = {
2 | hidden: { opacity: 0 },
3 | visible: {
4 | opacity: 1,
5 | transition: {
6 | staggerChildren: 0.1,
7 | delayChildren: 0.2
8 | }
9 | }
10 | }
11 |
12 | export const itemVariants = {
13 | hidden: { y: 20, opacity: 0 },
14 | visible: (custom: number) => ({
15 | y: 0,
16 | opacity: 1,
17 | transition: {
18 | type: 'spring',
19 | stiffness: 100,
20 | delay: custom * 0.1
21 | }
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as AvatarPrimitive from '@radix-ui/react-avatar'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 | ;('')
8 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 | ))
21 | Avatar.displayName = AvatarPrimitive.Root.displayName
22 |
23 | const AvatarImage = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
34 |
35 | const AvatarFallback = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49 |
50 | export { Avatar, AvatarFallback, AvatarImage }
51 |
--------------------------------------------------------------------------------
/src/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import { cva, type VariantProps } from 'class-variance-authority'
2 | import * as React from 'react'
3 |
4 | import { cn } from 'cn'
5 |
6 | const badgeVariants = cva(
7 | 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | 'border-transparent bg-foreground text-neutral-100 hover:bg-foreground/80',
13 | secondary:
14 | 'border-transparent bg-neutral-200 text-secondary-foreground hover:bg-neutral-200/80',
15 | destructive:
16 | 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
17 | outline: 'text-foreground'
18 | }
19 | },
20 | defaultVariants: {
21 | variant: 'default'
22 | }
23 | }
24 | )
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | const Badge = ({ className, variant, ...props }: BadgeProps) => {
31 | return (
32 |
33 | )
34 | }
35 | Badge.displayName = 'Badge'
36 |
37 | export { Badge, badgeVariants }
38 |
--------------------------------------------------------------------------------
/src/components/ui/checkbox.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
4 | import { Check } from 'lucide-react'
5 | import * as React from 'react'
6 |
7 | import { cn } from 'cn'
8 | ;('')
9 | const Checkbox = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => (
13 |
21 |
24 |
25 |
26 |
27 | ))
28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName
29 |
30 | export { Checkbox }
31 |
--------------------------------------------------------------------------------
/src/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/src/components/ui/hover-card.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as HoverCardPrimitive from '@radix-ui/react-hover-card'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 |
8 | const HoverCard = HoverCardPrimitive.Root
9 |
10 | const HoverCardTrigger = HoverCardPrimitive.Trigger
11 |
12 | const HoverCardContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
16 |
26 | ))
27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
28 |
29 | export { HoverCard, HoverCardContent, HoverCardTrigger }
30 |
--------------------------------------------------------------------------------
/src/components/ui/icon.tsx:
--------------------------------------------------------------------------------
1 | import { icons } from 'lucide-react'
2 |
3 | interface Props {
4 | name: keyof typeof icons
5 | color?: string
6 | size?: number
7 | strokeWidth?: number
8 | }
9 |
10 | export const Icon: React.FC = ({ name, color, size, strokeWidth }) => {
11 | // eslint-disable-next-line security/detect-object-injection
12 | const LucideIcon = icons[name]
13 |
14 | return (
15 |
16 | )
17 | }
18 |
19 | export type IconNames = keyof typeof icons
20 | export const iconNames = Object.keys(icons) as IconNames[]
21 |
--------------------------------------------------------------------------------
/src/components/ui/index.ts:
--------------------------------------------------------------------------------
1 | export * from './accordion'
2 | export * from './alert'
3 | export * from './avatar'
4 | export * from './badge'
5 | export * from './breadcrumb'
6 | export * from './button'
7 | export * from './calendar'
8 | export * from './card'
9 | export * from './checkbox'
10 | export * from './collapsible'
11 | export * from './color-picker'
12 | export * from './context-menu'
13 | export * from './dialog'
14 | export * from './dropdown-menu'
15 | export * from './form'
16 | export * from './icon'
17 | export * from './input'
18 | export * from './label'
19 | export * from './pagination'
20 | export * from './popover'
21 | export * from './progress'
22 | export * from './radio-group'
23 | export * from './resizable'
24 | export * from './scroll-area'
25 | export * from './select'
26 | export * from './separator'
27 | export * from './simple-checkbox'
28 | export * from './skeleton'
29 | export * from './slider'
30 | export * from './sonner'
31 | export * from './spinner'
32 | export * from './switch'
33 | export * from './table'
34 | export * from './tabs'
35 | export * from './textarea'
36 | export * from './ThemeToggle'
37 | export * from './toggle'
38 | export * from './tooltip'
39 |
--------------------------------------------------------------------------------
/src/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | import { cn } from 'cn'
4 | ;('')
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {
7 | icon?: React.ReactNode
8 | }
9 |
10 | const Input = React.forwardRef(
11 | ({ className, type, icon, ...props }, ref) => {
12 | return (
13 |
14 | {icon &&
{icon}
}
15 |
24 |
25 | )
26 | }
27 | )
28 | Input.displayName = 'Input'
29 |
30 | export { Input }
31 |
--------------------------------------------------------------------------------
/src/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as LabelPrimitive from '@radix-ui/react-label'
4 | import { cva, type VariantProps } from 'class-variance-authority'
5 | import * as React from 'react'
6 |
7 | import { cn } from 'cn'
8 | ;('')
9 | const labelVariants = cva(
10 | 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
11 | )
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ))
24 | Label.displayName = LabelPrimitive.Root.displayName
25 |
26 | export { Label }
27 |
--------------------------------------------------------------------------------
/src/components/ui/marquee.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 |
3 | interface MarqueeProps {
4 | className?: string
5 | reverse?: boolean
6 | pauseOnHover?: boolean
7 | children?: React.ReactNode
8 | vertical?: boolean
9 | repeat?: number
10 | [key: string]: any
11 | }
12 |
13 | export default function Marquee({
14 | className,
15 | reverse,
16 | pauseOnHover = false,
17 | children,
18 | vertical = false,
19 | repeat = 4,
20 | ...props
21 | }: MarqueeProps) {
22 | return (
23 |
34 | {Array(repeat)
35 | .fill(0)
36 | .map((_, i) => (
37 |
47 | {children}
48 |
49 | ))}
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as PopoverPrimitive from '@radix-ui/react-popover'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 | ;('')
8 | const Popover = PopoverPrimitive.Root
9 |
10 | const PopoverTrigger = PopoverPrimitive.Trigger
11 |
12 | const PopoverContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
16 |
17 |
27 |
28 | ))
29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName
30 |
31 | export { Popover, PopoverContent, PopoverTrigger }
32 |
--------------------------------------------------------------------------------
/src/components/ui/progress.tsx:
--------------------------------------------------------------------------------
1 | import * as ProgressPrimitive from '@radix-ui/react-progress'
2 | import * as React from 'react'
3 |
4 | import { cn } from 'cn'
5 |
6 | const Progress = React.forwardRef<
7 | React.ElementRef,
8 | React.ComponentPropsWithoutRef
9 | >(({ className, value, ...props }, ref) => (
10 |
18 |
22 |
23 | ))
24 | Progress.displayName = ProgressPrimitive.Root.displayName
25 |
26 | export { Progress }
27 |
--------------------------------------------------------------------------------
/src/components/ui/radio-group.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'
4 | import { Circle } from 'lucide-react'
5 | import * as React from 'react'
6 |
7 | import { cn } from 'cn'
8 | ;('')
9 | const RadioGroup = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => {
13 | return (
14 |
19 | )
20 | })
21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
22 |
23 | const RadioGroupItem = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => {
27 | return (
28 |
36 |
37 |
38 |
39 |
40 | )
41 | })
42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
43 |
44 | export { RadioGroup, RadioGroupItem }
45 |
--------------------------------------------------------------------------------
/src/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as SeparatorPrimitive from '@radix-ui/react-separator'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = 'horizontal', decorative = true, ...props },
14 | ref
15 | ) => (
16 |
29 | )
30 | )
31 | Separator.displayName = SeparatorPrimitive.Root.displayName
32 |
33 | export { Separator }
34 |
--------------------------------------------------------------------------------
/src/components/ui/simple-checkbox.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 | import React from 'react'
3 |
4 | interface SimpleCheckboxProps {
5 | label: string
6 | checked: boolean
7 | onCheckedChange: (checked: boolean) => void
8 | className?: string
9 | }
10 |
11 | const SimpleCheckbox: React.FC = ({
12 | label,
13 | checked,
14 | onCheckedChange,
15 | className,
16 | ...props
17 | }) => {
18 | const lowerCaseLabel = label.toLowerCase()
19 |
20 | const handleChange = (event: React.ChangeEvent) => {
21 | onCheckedChange(event.target.checked)
22 | }
23 |
24 | return (
25 |
46 | )
47 | }
48 |
49 | export default SimpleCheckbox
50 |
--------------------------------------------------------------------------------
/src/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from 'cn'
2 |
3 | function Skeleton({
4 | className,
5 | noPulse = false,
6 | ...props
7 | }: React.HTMLAttributes & { noPulse?: boolean }) {
8 | return (
9 |
17 | )
18 | }
19 |
20 | export { Skeleton }
21 |
--------------------------------------------------------------------------------
/src/components/ui/slider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as SliderPrimitive from '@radix-ui/react-slider'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 | ;('')
8 | const Slider = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
21 |
22 |
23 |
24 |
25 | ))
26 | Slider.displayName = SliderPrimitive.Root.displayName
27 |
28 | export { Slider }
29 |
--------------------------------------------------------------------------------
/src/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useTheme } from 'next-themes'
4 | import { Toaster as Sonner } from 'sonner'
5 |
6 | type ToasterProps = React.ComponentProps
7 |
8 | const Toaster = ({ ...props }: ToasterProps) => {
9 | const { theme = 'system' } = useTheme()
10 |
11 | return (
12 |
27 | )
28 | }
29 |
30 | export { Toaster }
31 |
--------------------------------------------------------------------------------
/src/components/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as SwitchPrimitives from '@radix-ui/react-switch'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 |
8 | const Switch = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
25 |
26 | ))
27 | Switch.displayName = SwitchPrimitives.Root.displayName
28 |
29 | export { Switch }
30 |
--------------------------------------------------------------------------------
/src/components/ui/textarea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | import { cn } from 'cn'
4 | ;('')
5 | export interface TextareaProps
6 | extends React.TextareaHTMLAttributes {}
7 |
8 | const Textarea = React.forwardRef(
9 | ({ className, ...props }, ref) => {
10 | return (
11 |
19 | )
20 | }
21 | )
22 | Textarea.displayName = 'Textarea'
23 |
24 | export { Textarea }
25 |
--------------------------------------------------------------------------------
/src/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import { Toaster as Sonner } from 'sonner'
5 |
6 | const Toaster = ({ ...props }: React.ComponentProps) => {
7 | return (
8 |
23 | )
24 | }
25 | Toaster.displayName = 'Toaster'
26 |
27 | export { Toaster }
28 |
--------------------------------------------------------------------------------
/src/components/ui/toggle.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as TogglePrimitive from '@radix-ui/react-toggle'
4 | import { cva, type VariantProps } from 'class-variance-authority'
5 | import * as React from 'react'
6 |
7 | import { cn } from 'cn'
8 | ;('')
9 | const toggleVariants = cva(
10 | 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-title text-subtitle focus-visible:outline-none focus-visible:ring-2 focus-visible:bing-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
11 | {
12 | variants: {
13 | variant: {
14 | default: 'bg-transparent',
15 | outline:
16 | 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground'
17 | },
18 | size: {
19 | default: 'h-10 px-3',
20 | sm: 'h-9 px-2.5',
21 | lg: 'h-11 px-5'
22 | }
23 | },
24 | defaultVariants: {
25 | variant: 'default',
26 | size: 'default'
27 | }
28 | }
29 | )
30 |
31 | const Toggle = React.forwardRef<
32 | React.ElementRef,
33 | React.ComponentPropsWithoutRef &
34 | VariantProps
35 | >(({ className, variant, size, ...props }, ref) => (
36 |
41 | ))
42 |
43 | Toggle.displayName = TogglePrimitive.Root.displayName
44 |
45 | export { Toggle, toggleVariants }
46 |
--------------------------------------------------------------------------------
/src/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as TooltipPrimitive from '@radix-ui/react-tooltip'
4 | import * as React from 'react'
5 |
6 | import { cn } from 'cn'
7 | ;('')
8 | const TooltipProvider = TooltipPrimitive.Provider
9 |
10 | const Tooltip = TooltipPrimitive.Root
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger
13 |
14 | const TooltipContent = React.forwardRef<
15 | React.ElementRef,
16 | React.ComponentPropsWithoutRef
17 | >(({ className, sideOffset = 4, ...props }, ref) => (
18 |
27 | ))
28 | TooltipContent.displayName = TooltipPrimitive.Content.displayName
29 |
30 | export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
31 |
--------------------------------------------------------------------------------
/src/components/ui/useFormField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { useFormContext } from 'react-hook-form'
3 |
4 | import { FormFieldContext, FormItemContext } from './form'
5 |
6 | const useFormField = () => {
7 | const fieldContext = React.useContext(FormFieldContext)
8 | const itemContext = React.useContext(FormItemContext)
9 | const { getFieldState, formState } = useFormContext()
10 |
11 | const fieldState = getFieldState(fieldContext.name, formState)
12 |
13 | if (!fieldContext) {
14 | throw new Error('useFormField should be used within ')
15 | }
16 |
17 | const { id } = itemContext
18 |
19 | return {
20 | id,
21 | name: fieldContext.name,
22 | formItemId: `${id}-form-item`,
23 | formDescriptionId: `${id}-form-item-description`,
24 | formMessageId: `${id}-form-item-message`,
25 | ...fieldState
26 | }
27 | }
28 |
29 | export { useFormField }
30 |
--------------------------------------------------------------------------------
/src/config/nav.ts:
--------------------------------------------------------------------------------
1 | import { SidebarLink } from '@/components/SidebarItems'
2 | import { Cog, Globe, HomeIcon } from 'lucide-react'
3 |
4 | type AdditionalLinks = {
5 | title: string
6 | links: SidebarLink[]
7 | }
8 |
9 | export const defaultLinks: SidebarLink[] = [
10 | { href: '/dashboard', title: 'Home', icon: HomeIcon },
11 | { href: '/account', title: 'Account', icon: Cog },
12 | { href: '/settings', title: 'Settings', icon: Cog }
13 | ]
14 |
15 | export const additionalLinks: AdditionalLinks[] = []
16 |
--------------------------------------------------------------------------------
/src/core/config/fonts/font.bricolage_grotesque.ts:
--------------------------------------------------------------------------------
1 | /* Font used in landing page */
2 |
3 | import { Bricolage_Grotesque } from 'next/font/google'
4 |
5 | export const bricolageGrotesque = Bricolage_Grotesque({
6 | subsets: ['latin'],
7 | display: 'swap',
8 | weight: ['400', '500']
9 | })
10 |
--------------------------------------------------------------------------------
/src/core/config/fonts/font.pt_mono.ts:
--------------------------------------------------------------------------------
1 | import { PT_Mono } from 'next/font/google'
2 |
3 | export const ptMono = PT_Mono({
4 | weight: ['400'],
5 | subsets: ['latin'],
6 | display: 'swap'
7 | })
8 |
--------------------------------------------------------------------------------
/src/core/config/fonts/fonts.ts:
--------------------------------------------------------------------------------
1 | import { bricolageGrotesque } from './font.bricolage_grotesque'
2 | import { bricolage } from './fonts'
3 |
4 | export { ptMono } from './font.pt_mono'
5 | export { geistMono } from './geist/font.geist'
6 | export { geistSans } from './geist/font.geist'
7 | export {
8 | bricolageGrotesque as bricolage,
9 | bricolageGrotesque as landing
10 | } from './font.bricolage_grotesque'
11 |
--------------------------------------------------------------------------------
/src/core/config/fonts/geist/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/config/fonts/geist/GeistMonoVF.woff
--------------------------------------------------------------------------------
/src/core/config/fonts/geist/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/config/fonts/geist/GeistVF.woff
--------------------------------------------------------------------------------
/src/core/config/fonts/geist/font.geist.ts:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | import localFont from 'next/font/local'
3 |
4 | export const geistSans = localFont({
5 | src: './GeistVF.woff',
6 | variable: '--font-geist-sans',
7 | weight: '100 900'
8 | })
9 |
10 | export const geistMono = localFont({
11 | src: './GeistMonoVF.woff',
12 | variable: '--font-geist-mono',
13 | weight: '100 900'
14 | })
15 |
--------------------------------------------------------------------------------
/src/core/config/locales/en.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | hero: {
3 | title: 'Notevault, a place for everything related to being effective',
4 | subtitle:
5 | 'Your dashboard: a central hub for digital life. Manage notes, files, and tools in one place. Boost productivity with a streamlined interface. ',
6 | subtitle2:
7 | ' Built by a developer, for myself.Without the well- known annoyance of having to wait for Cloudflare security checks, ads or reCAPTCHAs.',
8 | button: 'Get Started',
9 | stats: {
10 | lastCommit: 'Last commit',
11 | totalCommits: 'Total commits',
12 | codingStreak: 'Coding streak',
13 | daysUnit: 'days'
14 | }
15 | },
16 | githubStats: {
17 | madeBy: 'Made by',
18 | lastCommitDay: 'day',
19 | lastCommitHours: 'hours',
20 | lastCommitMinutes: 'minutes',
21 | ago: 'ago'
22 | },
23 | githubOpenSource: {
24 | title: 'Proudly OpenSource.',
25 | subtitle:
26 | 'Documenting victories and valuable failures. Our open-source saga, uncut and unfiltered.',
27 | button: 'Get Started',
28 | altText: 'Github logo'
29 | },
30 | signUpPage: {
31 | title: 'Sign up for an account',
32 | emailLabel: 'Your email',
33 | emailPlaceholder: 'name@company.com',
34 | passwordLabel: 'Password',
35 | passwordPlaceholder: '••••••••',
36 | submitButton: 'Create an account'
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/core/config/locales/locales.ts:
--------------------------------------------------------------------------------
1 | export * as en from './en'
2 |
--------------------------------------------------------------------------------
/src/core/config/menu-items/dashboard-navigation-menu-items.ts:
--------------------------------------------------------------------------------
1 | import { siteConfig } from '@/config/site-config'
2 | import { Github, HelpCircle } from 'lucide-react'
3 |
4 | export const links = [
5 | { href: '/features', label: 'Features' },
6 | { href: '/dashboard', label: 'Dashboard' },
7 | { href: '/dashboard/folders', label: 'Folders feature' }
8 | ]
9 |
10 | export const IconTooltips = [
11 | { href: '#', label: 'Help', icon: HelpCircle, isButton: true },
12 | {
13 | href: siteConfig.projects.main.url,
14 | label: 'GitHub',
15 | icon: Github,
16 | isButton: true
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/src/core/config/menu-items/design-system-menu.items.ts:
--------------------------------------------------------------------------------
1 | const prefix = '/design-system'
2 |
3 | export const designSystemItems = [
4 | { href: '/notice', label: 'Notice block', alias: 'Notice block' }
5 | ].map(item => ({ ...item, href: `${prefix}${item.href}` }))
6 |
--------------------------------------------------------------------------------
/src/core/config/metadata/dashboard.metadata.ts:
--------------------------------------------------------------------------------
1 | import type { Metadata } from 'next'
2 | import { metadata as rootMetadata } from './metadata.root-layout'
3 |
4 | const dashboardName = 'NoteVault Dashboard'
5 | const dashboardDescription =
6 | 'Manage your notes and account in the NoteVault dashboard'
7 |
8 | export const dashboardMetadata: Metadata = {
9 | ...rootMetadata,
10 | title: {
11 | default: dashboardName,
12 | template: `%s | ${dashboardName}`
13 | },
14 | description: dashboardDescription,
15 | openGraph: {
16 | ...rootMetadata.openGraph,
17 | title: dashboardName,
18 | description: dashboardDescription,
19 | images: [
20 | {
21 | url: `${rootMetadata.metadataBase}/dashboard-og-image.png`,
22 | width: 1200,
23 | height: 630,
24 | alt: 'NoteVault Dashboard'
25 | }
26 | ]
27 | },
28 | twitter: {
29 | ...rootMetadata.twitter,
30 | title: dashboardName,
31 | description: dashboardDescription,
32 | images: [`${rootMetadata.metadataBase}/dashboard-twitter-image.png`]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/core/config/nav.ts:
--------------------------------------------------------------------------------
1 | import { SidebarLink } from '@/components/SidebarItems'
2 | import { Cog, Globe, HomeIcon } from 'lucide-react'
3 |
4 | type AdditionalLinks = {
5 | title: string
6 | links: SidebarLink[]
7 | }
8 |
9 | export const defaultLinks: SidebarLink[] = [
10 | { href: '/dashboard', title: 'Home', icon: HomeIcon },
11 | { href: '/account', title: 'Account', icon: Cog },
12 | { href: '/settings', title: 'Settings', icon: Cog }
13 | ]
14 |
15 | export const additionalLinks: AdditionalLinks[] = []
16 |
--------------------------------------------------------------------------------
/src/core/constants/animations.ts:
--------------------------------------------------------------------------------
1 | import { cubicBezier, Variants } from 'framer-motion'
2 |
3 | export const fadeInUp = (delay: number = 0): Variants => ({
4 | hidden: { opacity: 0, y: 20 },
5 | visible: {
6 | opacity: 1,
7 | y: 0,
8 | transition: { duration: 0.3, delay, ease: 'easeOut' }
9 | }
10 | })
11 |
12 | export const customEasing = cubicBezier(0.35, 0.9, 0.13, 0.6)
13 |
--------------------------------------------------------------------------------
/src/core/constants/cn-tw.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from 'clsx'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/src/core/constants/generate-uuid.ts:
--------------------------------------------------------------------------------
1 | import { randomBytes } from 'crypto'
2 |
3 | export function generateUUID(): string {
4 | const bytes = randomBytes(16)
5 | const hex = bytes.toString('hex')
6 | return [
7 | hex.substring(0, 8),
8 | hex.substring(8, 12),
9 | hex.substring(12, 16),
10 | hex.substring(16, 20),
11 | hex.substring(20, 32)
12 | ].join('-')
13 | }
14 |
15 | // await db.insert(activityLogs).values({
16 | // id: generateUUID(), // Use generateUUID to create a unique ID
17 | // userId: userId ?? session.user.id,
18 | // action: result.data.action,
19 | // details: result.data.details
20 | // })
21 | // }
22 |
--------------------------------------------------------------------------------
/src/core/hooks/_hook_helpers/use-event-callback.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useCallback, useRef } from 'react'
4 | import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect'
5 |
6 | export function useEventCallback(
7 | fn: (...args: Args) => R
8 | ): (...args: Args) => R
9 | export function useEventCallback(
10 | fn: ((...args: Args) => R) | undefined
11 | ): ((...args: Args) => R) | undefined
12 | export function useEventCallback(
13 | fn: ((...args: Args) => R) | undefined
14 | ): ((...args: Args) => R) | undefined {
15 | const ref = useRef(() => {
16 | throw new Error('Cannot call an event handler while rendering.')
17 | })
18 |
19 | useIsomorphicLayoutEffect(() => {
20 | ref.current = fn
21 | }, [fn])
22 |
23 | //!! To check
24 | // biome-ignore lint/correctness/useExhaustiveDependencies:
25 | return useCallback((...args: Args) => ref.current?.(...args), [ref]) as (
26 | ...args: Args
27 | ) => R
28 | }
29 |
--------------------------------------------------------------------------------
/src/core/hooks/_hook_helpers/use-isomorphic-layout-effect.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useEffect, useLayoutEffect } from 'react'
4 |
5 | export const useIsomorphicLayoutEffect =
6 | typeof window !== 'undefined' ? useLayoutEffect : useEffect
7 |
--------------------------------------------------------------------------------
/src/core/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './_hook_helpers/use-isomorphic-layout-effect'
2 | export * from './use-copy-clipboard'
3 | export * from './use-dev-mode'
4 | export * from './use-geo-location'
5 | export * from './use-keyboard-shortcuts'
6 | export * from './use-local-storage'
7 | export * from './use-mouse-hover'
8 | export * from './use-skeleton-loader'
9 | export * from './use-user-info'
10 |
--------------------------------------------------------------------------------
/src/core/hooks/use-copy-clipboard.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useCallback, useEffect, useState } from 'react'
4 |
5 | type CopiedValue = string | null
6 |
7 | type CopyFn = (text: string) => Promise
8 |
9 | export function useCopyToClipboard({
10 | isCopiedDelay = 2000
11 | }: {
12 | isCopiedDelay?: number
13 | } = {}): [CopiedValue, CopyFn, boolean] {
14 | const [copiedText, setCopiedText] = useState(null)
15 | const [isCopied, setIsCopied] = useState(false)
16 |
17 | useEffect(() => {
18 | if (!isCopied) {
19 | return
20 | }
21 | setTimeout(() => {
22 | setIsCopied(false)
23 | }, isCopiedDelay)
24 | }, [isCopied, isCopiedDelay])
25 |
26 | const copy: CopyFn = useCallback(async text => {
27 | if (!navigator?.clipboard) {
28 | return false
29 | }
30 |
31 | // Try to save to clipboard then save it in the state if worked
32 | try {
33 | await navigator.clipboard.writeText(text)
34 | setCopiedText(text)
35 | setIsCopied(true)
36 | return true
37 | } catch (_error) {
38 | setCopiedText(null)
39 | return false
40 | }
41 | }, [])
42 |
43 | return [copiedText, copy, isCopied]
44 | }
45 |
--------------------------------------------------------------------------------
/src/core/hooks/use-mouse-hover.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useEffect, useRef } from 'react'
4 |
5 | export default function useMouseHoverEffect() {
6 | const elementRef = useRef(null)
7 |
8 | useEffect(() => {
9 | const element = elementRef.current
10 | if (!element) return
11 |
12 | const handleMouseMove = (e: MouseEvent) => {
13 | const rect = element.getBoundingClientRect()
14 | const x = e.clientX - rect.left
15 | const y = e.clientY - rect.top
16 |
17 | element.style.setProperty('--mouse-x', `${x}px`)
18 | element.style.setProperty('--mouse-y', `${y}px`)
19 | }
20 |
21 | element.addEventListener('mousemove', handleMouseMove)
22 |
23 | // Add border class
24 | element.classList.add('border', 'border-white-012')
25 |
26 | return () => {
27 | element.removeEventListener('mousemove', handleMouseMove)
28 | element.classList.remove('border', 'border-white-012')
29 | }
30 | }, [])
31 |
32 | return elementRef
33 | }
34 |
--------------------------------------------------------------------------------
/src/core/hooks/use-skeleton-loader.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useEffect, useState } from 'react'
4 |
5 | interface SkeletonLoaderOptions {
6 | fetchData: () => Promise<{ [key: string]: T[] }>
7 | getItemCount: () => Promise
8 | }
9 |
10 | export function useSkeletonLoader({
11 | fetchData,
12 | getItemCount
13 | }: SkeletonLoaderOptions) {
14 | const [data, setData] = useState([])
15 | const [isLoading, setIsLoading] = useState(true)
16 | const [itemCount, setItemCount] = useState(0)
17 |
18 | useEffect(() => {
19 | async function load() {
20 | try {
21 | setIsLoading(true)
22 | const count = await getItemCount()
23 | setItemCount(count)
24 | const result = await fetchData()
25 | setData(Object.values(result)[0])
26 | } catch (error) {
27 | console.error('Error fetching data:', error)
28 | } finally {
29 | setIsLoading(false)
30 | }
31 | }
32 |
33 | load()
34 | }, [fetchData, getItemCount])
35 |
36 | return { data, isLoading, itemCount }
37 | }
38 |
--------------------------------------------------------------------------------
/src/core/models/activities.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod'
2 |
3 | export const createActivitySchema = z.object({
4 | name: z.string().min(2, 'Name must be at least 2 characters'),
5 | duration: z.number().min(1, 'Duration must be at least 1 minute'),
6 | icon: z.string(),
7 | color: z.string()
8 | })
9 |
--------------------------------------------------------------------------------
/src/core/models/folders.z.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod'
2 |
3 | export const folderSchema = z.object({
4 | name: z.string().min(1, 'Name is required'),
5 | color: z.string().optional(),
6 | description: z.string().optional(),
7 | parentId: z.string().nullable().optional()
8 | })
9 |
10 | export const updateFolderSchema = z.object({
11 | id: z.string(),
12 | name: z.string().min(1).max(255).optional(),
13 | color: z
14 | .string()
15 | .regex(/^#[0-9A-Fa-f]{6}$/, 'Must be a valid hex color')
16 | .optional(),
17 | description: z.string().nullable().optional()
18 | })
19 |
--------------------------------------------------------------------------------
/src/core/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './folders.z'
2 |
--------------------------------------------------------------------------------
/src/core/scripts/fix-imports/optimize-iu-imports.mdx:
--------------------------------------------------------------------------------
1 | # UI Import Consolidator
2 |
3 | Replaces multiple '@/components/ui' imports:
4 |
5 | ```
6 | import { Button } from '@/components/ui/button'
7 | import { Card } from '@/components/ui/card'
8 | ```
9 |
10 | With a single import:
11 |
12 | ```
13 | import { Button, Card } from 'ui'
14 | ```
15 |
16 | Requires:
17 |
18 | - index.ts in ui folder exporting all components
19 | - 'ui' alias in tsconfig.json linking to the index.ts file in components/ui
20 | Run:
21 |
22 | ```
23 | python consolidate_imports.py --folder src/app/feature
24 | ```
25 |
--------------------------------------------------------------------------------
/src/core/server/actions/activities/create-activity.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { createActivitySchema } from '@/core/models/activities'
4 | import { db } from 'db'
5 | import { activities } from '@/core/server/db/schema'
6 | import { revalidatePath } from 'next/cache'
7 |
8 | export async function createActivity(formData: FormData) {
9 | const validatedFields = createActivitySchema.safeParse({
10 | name: formData.get('name'),
11 | duration: Number(formData.get('duration')),
12 | icon: formData.get('icon'),
13 | color: formData.get('color')
14 | })
15 |
16 | if (!validatedFields.success) {
17 | return { error: validatedFields.error.flatten().fieldErrors }
18 | }
19 |
20 | const { name, duration, icon, color } = validatedFields.data
21 |
22 | try {
23 | await db.insert(activities).values({
24 | name,
25 | duration,
26 | icon,
27 | color
28 | })
29 | } catch (error) {
30 | return { error: 'Failed to create activity' }
31 | }
32 |
33 | revalidatePath('/dashboard')
34 | return { success: true }
35 | }
36 |
--------------------------------------------------------------------------------
/src/core/server/actions/activity-log/fetch-activity.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { getUserAuth } from '@/core/server/auth/utils'
4 |
5 | import { activityLogs, db } from 'db'
6 | import { desc, eq } from 'drizzle-orm'
7 |
8 | export async function getActivityLogs() {
9 | const { session } = await getUserAuth()
10 | if (!session) throw new Error('Unauthorized')
11 |
12 | const logs = await db
13 | .select()
14 | .from(activityLogs)
15 | .where(eq(activityLogs.userId, session.user.id))
16 | .orderBy(desc(activityLogs.timestamp))
17 | .limit(50)
18 |
19 | return logs
20 | }
21 |
--------------------------------------------------------------------------------
/src/core/server/actions/activity-log/index.ts:
--------------------------------------------------------------------------------
1 | export * from './fetch-activity'
2 | export * from './log-activity'
3 |
--------------------------------------------------------------------------------
/src/core/server/actions/activity-log/log-activity.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { getUserAuth } from '@/core/server/auth/utils'
4 | import { activityLogs } from '@/core/server/db/schema/activity'
5 |
6 | import { db } from 'db'
7 | import { generateId } from 'lucia'
8 |
9 | export async function logActivity(
10 | action: string,
11 | details?: string,
12 | userId?: string,
13 | metadata?: Record
14 | ) {
15 | try {
16 | const { session } = await getUserAuth()
17 | if (!session) {
18 | throw new Error('User not authenticated')
19 | }
20 |
21 | const userId = session.user.id
22 |
23 | const [newLog] = await db
24 | .insert(activityLogs)
25 | .values({
26 | id: generateId(15),
27 | userId,
28 | action,
29 | details: details || '',
30 | metadata: metadata ? JSON.stringify(metadata) : null,
31 | timestamp: new Date()
32 | })
33 | .returning()
34 |
35 | return { success: true, log: newLog }
36 | } catch (error) {
37 | const errorLog = {
38 | timestamp: new Date().toISOString(),
39 | errorMessage: (error as Error).message,
40 | errorStack: (error as Error).stack,
41 | action,
42 | userId,
43 | details,
44 | metadata
45 | }
46 | console.warn(
47 | 'Failed to log activity:',
48 | JSON.stringify(errorLog, null, 2)
49 | )
50 | return {
51 | success: false,
52 | error: 'Failed to log activity',
53 | errorDetails: errorLog
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/core/server/actions/activity.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { getUserAuth } from '@/core/server/auth/utils'
4 | import {
5 | activityLogs,
6 | activityLogSchema
7 | } from '@/core/server/db/schema/activity'
8 |
9 | import { db } from 'db'
10 | import { desc, eq } from 'drizzle-orm'
11 | import { generateId } from 'lucia'
12 |
13 | export async function logActivity(
14 | action: string,
15 | details?: string,
16 | userId?: string,
17 | p0?: { attemptedUrl: string; timestamp: string }
18 | ) {
19 | const { session } = await getUserAuth()
20 | if (!session) throw new Error('Unauthorized')
21 |
22 | const result = activityLogSchema.safeParse({ action, details })
23 | if (!result.success) {
24 | throw new Error('Invalid activity log data')
25 | }
26 |
27 | await db.insert(activityLogs).values({
28 | id: generateId(15),
29 | userId: userId ?? session.user.id,
30 | action: result.data.action,
31 | details: result.data.details
32 | })
33 | }
34 |
35 | export async function getActivityLogs() {
36 | const { session } = await getUserAuth()
37 | if (!session) throw new Error('Unauthorized')
38 |
39 | const logs = await db
40 | .select()
41 | .from(activityLogs)
42 | .where(eq(activityLogs.userId, session.user.id))
43 | .orderBy(desc(activityLogs.timestamp))
44 | .limit(50)
45 |
46 | return logs
47 | }
48 |
--------------------------------------------------------------------------------
/src/core/server/actions/finance/budget.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { getUserAuth } from '@/core/server/auth/utils'
4 | import { budgets } from '@/core/server/db/schema'
5 |
6 | import { db } from 'db'
7 | import { revalidatePath } from 'next/cache'
8 |
9 | export async function createMonthlyBudget(
10 | title: string,
11 | amount: string,
12 | month: string,
13 | description?: string
14 | ) {
15 | const { session } = await getUserAuth()
16 | if (!session) {
17 | throw new Error('You must be logged in to create a budget')
18 | }
19 |
20 | await db.insert(budgets).values({
21 | userId: session.user.id,
22 | title,
23 | amount,
24 | month,
25 | description
26 | })
27 |
28 | revalidatePath('/dashboard')
29 | }
30 |
31 | export async function getUserMonthlyBudgets() {
32 | const { session } = await getUserAuth()
33 | if (!session) {
34 | return []
35 | }
36 |
37 | return db.query.budgets.findMany({
38 | where: (budgets, { eq }) => eq(budgets.userId, session.user.id),
39 | orderBy: (budgets, { desc }) => [desc(budgets.month)]
40 | })
41 | }
42 |
--------------------------------------------------------------------------------
/src/core/server/actions/finance/goals.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { getUserAuth } from '@/core/server/auth/utils'
4 | import { goals } from '@/core/server/db/schema'
5 |
6 | import { db } from 'db'
7 | import { revalidatePath } from 'next/cache'
8 |
9 | export async function createGoal(title: string, targetAmount: string) {
10 | const { session } = await getUserAuth()
11 | if (!session) {
12 | throw new Error('You must be logged in to create a goal')
13 | }
14 |
15 | await db.insert(goals).values({
16 | userId: session.user.id,
17 | title,
18 | targetAmount,
19 | currentAmount: '0'
20 | })
21 |
22 | revalidatePath('/dashboard')
23 | }
24 |
25 | export async function getUserGoals() {
26 | const { session } = await getUserAuth()
27 | if (!session) {
28 | return []
29 | }
30 |
31 | return db.query.goals.findMany({
32 | where: (goals, { eq }) => eq(goals.userId, session.user.id),
33 | orderBy: (goals, { desc }) => [desc(goals.createdAt)]
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/src/core/server/actions/finance/index.ts:
--------------------------------------------------------------------------------
1 | export * from './budget'
2 | export * from './transactions'
3 | export * from './goals'
4 |
--------------------------------------------------------------------------------
/src/core/server/actions/finance/transactions.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { getUserAuth } from '@/core/server/auth/utils'
4 |
5 | import { db, transactions } from 'db'
6 | import { revalidatePath } from 'next/cache'
7 |
8 | export async function createTransaction(
9 | description: string,
10 | amount: number,
11 | date: string
12 | ) {
13 | const { session } = await getUserAuth()
14 | if (!session) {
15 | throw new Error('You must be logged in to create a transaction')
16 | }
17 |
18 | await db.insert(transactions).values({
19 | userId: session.user.id,
20 | description,
21 | amount,
22 | date: new Date(date)
23 | })
24 |
25 | revalidatePath('/dashboard')
26 | }
27 |
28 | export async function getRecentTransactions() {
29 | const { session } = await getUserAuth()
30 | if (!session) {
31 | return []
32 | }
33 |
34 | return db.query.transactions.findMany({
35 | where: (transactions, { eq }) =>
36 | eq(transactions.userId, session.user.id),
37 | orderBy: (transactions, { desc }) => [desc(transactions.date)],
38 | limit: 10
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/src/core/server/actions/get-visitory-city.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 | import { cache } from 'react'
3 |
4 | /**
5 | * This function fetches the visitor's city using the ipapi.co API.
6 | * It caches the result to avoid unnecessary API calls.
7 | * If the API call fails, it returns 'Unknown City'.
8 | * @returns {Promise} The visitor's city or 'Unknown City'.
9 | */
10 | export const getCity = cache(async () => {
11 | try {
12 | console.log('Fetching city data...')
13 | const response = await fetch('https://ipapi.co/json')
14 | if (!response.ok) {
15 | throw new Error('Failed to fetch city data')
16 | }
17 | const data = await response.json()
18 | console.log('Fetched city data:', data)
19 | return data.city || 'Unknown City'
20 | } catch (err) {
21 | console.error('Error fetching city data:', err)
22 | return 'Unknown City'
23 | }
24 | })
25 |
--------------------------------------------------------------------------------
/src/core/server/actions/github/index.ts:
--------------------------------------------------------------------------------
1 | export * from './fetch-github-stats'
2 |
--------------------------------------------------------------------------------
/src/core/server/actions/index.ts:
--------------------------------------------------------------------------------
1 | export * from './activities/create-activity'
2 | export * from './activity-log'
3 | export * from './finance'
4 | export * from './get-visitory-city'
5 | export * from './github'
6 | export * from './save-processed-text/save-processed-text'
7 | export * from './tasks'
8 | export * from './user'
9 |
--------------------------------------------------------------------------------
/src/core/server/actions/save-processed-text/save-processed-text.ts:
--------------------------------------------------------------------------------
1 | import { processedTexts } from '@/core/server/db/schema'
2 | import { db } from 'db'
3 |
4 | export async function saveProcessedText(
5 | name: string,
6 | content: string,
7 | processorType: string
8 | ) {
9 | try {
10 | const result = await db
11 | .insert(processedTexts)
12 | .values({
13 | name,
14 | content,
15 | processorType,
16 | createdAt: new Date()
17 | })
18 | .returning()
19 |
20 | return { success: true, data: result[0] }
21 | } catch (error) {
22 | console.error('Error saving processed text:', error)
23 | return {
24 | success: false,
25 | error: 'Failed to save processed text',
26 | details: error instanceof Error ? error.message : String(error)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/add-sub-task.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/add-sub-task.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/add-task-label.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/add-task-label.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/create-task.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/create-task.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/get-label.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/get-label.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/get-task.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/get-task.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/remove-task-label.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/remove-task-label.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/update-sub-task.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/update-sub-task.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/update-task-due-date.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/update-task-due-date.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/update-task-priority.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/update-task-priority.ts
--------------------------------------------------------------------------------
/src/core/server/actions/tasks/update-task-status.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remcostoeten/nextjs-lucia-neon-postgresql-drizzle-dashboard/ca67b1f795427ef68988a24567e6e2ed98aef495/src/core/server/actions/tasks/update-task-status.ts
--------------------------------------------------------------------------------
/src/core/server/actions/update-user-profile.ts:
--------------------------------------------------------------------------------
1 | import { db, users } from '@/core/server/db'
2 | import { eq } from 'drizzle-orm'
3 | import { z } from 'zod'
4 |
5 | const updateAccountSchema = z.object({
6 | email: z.string().email('Invalid email address')
7 | })
8 |
9 | export default async function updateAccount(
10 | data: z.infer,
11 | user: any
12 | ) {
13 | const { email } = data
14 |
15 | await db.update(users).set({ email }).where(eq(users.id, user.id))
16 |
17 | return { success: 'Account updated successfully.' }
18 | }
19 |
--------------------------------------------------------------------------------
/src/core/server/actions/user/action.sign-out.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { logActivity } from 'actions'
4 | import { redirect } from 'next/navigation'
5 | import { validateRequest, lucia } from '../../auth/lucia'
6 | import { setAuthCookie } from '../../auth/utils'
7 |
8 | export async function signOutAction(formData: FormData) {
9 | console.log('signOutAction called')
10 | const { session } = await validateRequest()
11 | if (!session) {
12 | console.log('No session found, redirecting to sign-in')
13 | return redirect('/sign-in?error=Unauthorized')
14 | }
15 |
16 | console.log('Invalidating session:', session.id)
17 | await lucia.invalidateSession(session.id)
18 |
19 | await logActivity(session.userId, 'Sign Out', 'User signed out', {
20 | timestamp: new Date().toISOString(),
21 | device: formData.get('device') as string,
22 | location: formData.get('location') as string,
23 | timezone: formData.get('timezone') as string,
24 | lastPage: formData.get('lastPage') as string,
25 | os: formData.get('os') as string
26 | })
27 |
28 | const sessionCookie = lucia.createBlankSessionCookie()
29 | setAuthCookie(sessionCookie)
30 | console.log('Redirecting to sign-in page')
31 | return redirect('/sign-in?success=Signed out successfully')
32 | }
33 |
--------------------------------------------------------------------------------
/src/core/server/actions/user/action.sign-up.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import { ActionResult } from '@/types/types.users'
4 | import { logActivity } from 'actions'
5 | import { db, users } from 'db'
6 | import { generateId } from 'lucia'
7 | import { Argon2id } from 'oslo/password'
8 | import { lucia } from '../../auth/lucia'
9 | import {
10 | genericError,
11 | setAuthCookie,
12 | validateAuthFormData
13 | } from '../../auth/utils'
14 |
15 | export default async function signUpAction(
16 | _: ActionResult,
17 | formData: FormData
18 | ): Promise {
19 | const { data, error } = validateAuthFormData(formData)
20 |
21 | if (error !== null) return { error, success: false }
22 |
23 | try {
24 | const hashedPassword = await new Argon2id().hash(data.password)
25 | const userId = generateId(15)
26 |
27 | await db.insert(users).values({
28 | id: userId,
29 | email: data.email,
30 | hashedPassword
31 | })
32 |
33 | const session = await lucia.createSession(userId, {})
34 |
35 | await logActivity('Sign Up', 'User registered successfully', userId, {
36 | email: data.email,
37 | timestamp: new Date().toISOString(),
38 | device: formData.get('device') as string,
39 | location: formData.get('location') as string,
40 | timezone: formData.get('timezone') as string,
41 | lastPage: formData.get('lastPage') as string,
42 | os: formData.get('os') as string
43 | })
44 |
45 | const sessionCookie = lucia.createSessionCookie(session.id)
46 | setAuthCookie(sessionCookie)
47 | return { userId }
48 | } catch (e) {
49 | console.error('Sign-up error:', e)
50 | return { ...genericError, success: false }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/core/server/actions/user/action.update-user.ts:
--------------------------------------------------------------------------------
1 | 'use server'
2 |
3 | import {
4 | updateUserProfileSchema,
5 | userProfiles
6 | } from '@/core/server/db/schema/auth'
7 | import { db } from 'db'
8 | import { eq } from 'drizzle-orm'
9 | import { revalidatePath } from 'next/cache'
10 | import { z } from 'zod'
11 |
12 | export async function updateProfile(
13 | data: z.infer
14 | ) {
15 | try {
16 | const validatedData = updateUserProfileSchema.parse(data)
17 |
18 | const existingProfile = await db.query.userProfiles.findFirst({
19 | where: eq(userProfiles.userId, validatedData.userId)
20 | })
21 |
22 | if (existingProfile) {
23 | // Update existing profile
24 | await db
25 | .update(userProfiles)
26 | .set(validatedData)
27 | .where(eq(userProfiles.userId, validatedData.userId))
28 | } else {
29 | // Create new profile
30 | await db.insert(userProfiles).values({
31 | ...validatedData,
32 | userId: validatedData.userId as string
33 | })
34 | }
35 |
36 | revalidatePath('/profile') // Adjust this path as needed
37 |
38 | return { success: true, message: 'Profile updated successfully' }
39 | } catch (error) {
40 | console.error('Error updating user profile:', error)
41 | return { success: false, message: 'Failed to update profile' }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/core/server/actions/user/index.ts:
--------------------------------------------------------------------------------
1 | export * from './action.sign-in'
2 | export * from './action.sign-out'
3 | export * from './action.sign-up'
4 | export * from './action.update-user'
5 |
--------------------------------------------------------------------------------
/src/core/server/auth/client-auth-utils.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useRouter } from 'next/navigation'
4 |
5 | export type ClientAuthSession = {
6 | user: {
7 | id: string
8 | name?: string
9 | email?: string
10 | username?: string
11 | } | null
12 | }
13 |
14 | export const useClientAuth = () => {
15 | const router = useRouter()
16 |
17 | const getClientSession = async (): Promise => {
18 | try {
19 | const response = await fetch('/api/auth/session', {
20 | method: 'GET',
21 | credentials: 'include'
22 | })
23 | if (!response.ok) {
24 | throw new Error('Failed to fetch session')
25 | }
26 | const data = await response.json()
27 | return { user: data.user || null }
28 | } catch (error) {
29 | console.error('Error fetching client session:', error)
30 | return { user: null }
31 | }
32 | }
33 |
34 | const clientSignOut = async () => {
35 | try {
36 | const response = await fetch('/api/auth/logout', {
37 | method: 'POST',
38 | credentials: 'include'
39 | })
40 | if (!response.ok) {
41 | throw new Error('Failed to sign out')
42 | }
43 | router.push('/') // Redirect to page after sign out
44 | } catch (error) {
45 | console.error('Error signing out:', error)
46 | }
47 | }
48 |
49 | const clientCheckAuth = async () => {
50 | const { user } = await getClientSession()
51 | if (!user) {
52 | router.push('/')
53 | }
54 | return user
55 | }
56 |
57 | return {
58 | getClientSession,
59 | clientSignOut,
60 | clientCheckAuth
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/core/server/db/index.ts:
--------------------------------------------------------------------------------
1 | import { env } from '@/lib/env.mjs'
2 | import { neon, NeonQueryFunction, Pool } from '@neondatabase/serverless'
3 | import { drizzle } from 'drizzle-orm/neon-http'
4 |
5 | // Import all schemas and relations
6 | import * as activity from './schema/activity'
7 | import * as auth from './schema/auth'
8 | import * as folders from './schema/folders'
9 | import * as notes from './schema/notes'
10 | const schema = {
11 | ...auth,
12 | ...folders,
13 | ...notes,
14 | ...activity
15 | }
16 |
17 | let sql: NeonQueryFunction
18 | let db: ReturnType
19 | let pool: Pool
20 |
21 | if (typeof window === 'undefined') {
22 | sql = neon(env.DATABASE_URL)
23 | db = drizzle(sql, { schema })
24 | pool = new Pool({ connectionString: env.DATABASE_URL })
25 | }
26 |
27 | export { db, pool, sql }
28 |
29 | export * from './schema/activity'
30 | export * from './schema/auth'
31 | export * from './schema/folders'
32 | // export * from './schema/notes'
33 | export * from './schema/finance'
34 | export * from './schema/processed-text'
35 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/activity.ts:
--------------------------------------------------------------------------------
1 | import { integer, jsonb, text, timestamp } from 'drizzle-orm/pg-core'
2 | import { pgTable } from 'drizzle-orm/pg-core/table'
3 | import { z } from 'zod'
4 | import { users } from './auth'
5 |
6 | export const activityLogs = pgTable('activity_logs', {
7 | id: text('id').primaryKey(),
8 | userId: text('user_id')
9 | .notNull()
10 | .references(() => users.id),
11 | action: text('action').notNull(),
12 | details: text('details'),
13 | metadata: jsonb('metadata'),
14 | timestamp: timestamp('timestamp').defaultNow().notNull()
15 | })
16 |
17 | export const activities = pgTable('activities', {
18 | id: text('id').primaryKey(),
19 | name: text('name').notNull(),
20 | duration: integer('duration').notNull(),
21 | icon: text('icon').notNull(),
22 | color: text('color').notNull(),
23 | createdAt: timestamp('created_at').defaultNow().notNull(),
24 | updatedAt: timestamp('updated_at').defaultNow().notNull()
25 | })
26 |
27 | export const activityLogSchema = z.object({
28 | action: z.string(),
29 | details: z.string().optional(),
30 | metadata: z.record(z.any()).optional()
31 | })
32 |
33 | export type ActivityLog = typeof activityLogs.$inferSelect
34 |
35 | export type Activity = typeof activities.$inferSelect
36 | export type InsertActivity = typeof activities.$inferInsert
37 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/finance/budget.ts:
--------------------------------------------------------------------------------
1 | import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
2 |
3 | export const budgets = pgTable('budgets', {
4 | id: uuid('id').defaultRandom().primaryKey(),
5 | userId: uuid('user_id').notNull(),
6 | title: text('title').notNull(),
7 | amount: text('amount').notNull(),
8 | month: text('month').notNull(),
9 | description: text('description'),
10 | createdAt: timestamp('created_at').defaultNow().notNull(),
11 | updatedAt: timestamp('updated_at').defaultNow().notNull()
12 | })
13 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/finance/goals.ts:
--------------------------------------------------------------------------------
1 | import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
2 |
3 | export const goals = pgTable('goals', {
4 | id: uuid('id').defaultRandom().primaryKey(),
5 | userId: uuid('user_id').notNull(),
6 | title: text('title').notNull(),
7 | targetAmount: text('target_amount').notNull(),
8 | currentAmount: text('current_amount').notNull(),
9 | createdAt: timestamp('created_at').defaultNow().notNull(),
10 | updatedAt: timestamp('updated_at').defaultNow().notNull()
11 | })
12 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/finance/index.ts:
--------------------------------------------------------------------------------
1 | export * from './budget'
2 | export * from './goals'
3 | export * from './transactions'
4 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/finance/transactions.ts:
--------------------------------------------------------------------------------
1 | import { integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
2 |
3 | export const transactions = pgTable('transactions', {
4 | id: uuid('id').defaultRandom().primaryKey(),
5 | userId: uuid('user_id').notNull(),
6 | description: text('description').notNull(),
7 | amount: integer('amount').notNull(),
8 | date: timestamp('date').notNull(),
9 | createdAt: timestamp('created_at').defaultNow().notNull(),
10 | updatedAt: timestamp('updated_at').defaultNow().notNull()
11 | })
12 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/folders.ts:
--------------------------------------------------------------------------------
1 | import {
2 | boolean,
3 | jsonb,
4 | pgTable,
5 | text,
6 | timestamp,
7 | uuid
8 | } from 'drizzle-orm/pg-core'
9 | import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
10 | import { z } from 'zod'
11 |
12 | export const folders = pgTable('folders', {
13 | id: uuid('id').defaultRandom().primaryKey(),
14 | name: text('name').notNull(),
15 | color: text('color').default('#000000').notNull(),
16 | createdAt: timestamp('created_at').defaultNow().notNull(),
17 | updatedAt: timestamp('updated_at').defaultNow().notNull(),
18 | userId: uuid('user_id').notNull()
19 | })
20 |
21 | export const notes = pgTable('notes', {
22 | id: uuid('id').defaultRandom().primaryKey(),
23 | title: text('title').notNull(),
24 | content: text('content').notNull(),
25 | folderId: uuid('folder_id')
26 | .references(() => folders.id)
27 | .notNull(),
28 | isPinned: boolean('is_pinned').default(false).notNull(),
29 | tags: jsonb('tags').default([]).notNull(),
30 | createdAt: timestamp('created_at').defaultNow().notNull(),
31 | updatedAt: timestamp('updated_at').defaultNow().notNull(),
32 | userId: uuid('user_id').notNull()
33 | })
34 |
35 | export const insertFolderSchema = createInsertSchema(folders).omit({
36 | id: true,
37 | createdAt: true,
38 | updatedAt: true
39 | })
40 | export const selectFolderSchema = createSelectSchema(folders)
41 |
42 | export const insertNoteSchema = createInsertSchema(notes).omit({
43 | id: true,
44 | createdAt: true,
45 | updatedAt: true
46 | })
47 | export const selectNoteSchema = createSelectSchema(notes)
48 |
49 | export type Folder = z.infer
50 | export type Note = z.infer
51 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/index.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm'
2 | import { keys, sessions, users } from './auth'
3 | import { folders, notes } from './notes'
4 | import { labels, taskLabels, tasks, taskUsersRelations } from './tasks'
5 |
6 | export { folders, keys, labels, notes, sessions, taskLabels, tasks, users }
7 |
8 | export const usersRelations = relations(users, ({ one, many }) => ({
9 | folders: many(folders),
10 | notes: many(notes),
11 | ...taskUsersRelations(users)
12 | }))
13 |
14 | export * from './activity'
15 | export * from './auth'
16 | export * from './finance'
17 | export * from './notes'
18 | export * from './parsed-ig'
19 | export * from './processed-text'
20 | export * from './tasks'
21 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/parsed-ig.ts:
--------------------------------------------------------------------------------
1 | import { generateUUID } from '@/core/constants/generate-uuid'
2 | import { pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'
3 | import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
4 | import { z } from 'zod'
5 | import { users } from './auth'
6 |
7 | export const parsedOutputs = pgTable('parsed_outputs', {
8 | id: varchar('id', { length: 191 })
9 | .primaryKey()
10 | .$defaultFn(() => generateUUID()),
11 | title: text('title').notNull(),
12 | content: text('content').notNull(),
13 | userId: varchar('user_id')
14 | .notNull()
15 | .references(() => users.id),
16 | createdAt: timestamp('created_at').defaultNow().notNull(),
17 | updatedAt: timestamp('updated_at').defaultNow().notNull()
18 | })
19 |
20 | export const insertParsedOutputSchema = createInsertSchema(parsedOutputs).omit({
21 | id: true,
22 | createdAt: true,
23 | updatedAt: true
24 | })
25 | export const selectParsedOutputSchema = createSelectSchema(parsedOutputs)
26 |
27 | export type ParsedOutput = z.infer
28 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/processed-text.ts:
--------------------------------------------------------------------------------
1 | import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
2 |
3 | export const processedTexts = pgTable('processed_texts', {
4 | id: uuid('id').defaultRandom().primaryKey(),
5 | name: text('name').notNull(),
6 | content: text('content').notNull(),
7 | processorType: text('processor_type').notNull(),
8 | userId: uuid('user_id').notNull(),
9 | createdAt: timestamp('created_at').defaultNow().notNull()
10 | })
11 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/tasks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './label'
2 | export * from './task'
3 | export * from './task-label'
4 |
5 | import { relations } from 'drizzle-orm'
6 | import { users } from '../auth'
7 | import { labels } from './label'
8 | import { tasks } from './task'
9 |
10 | export const taskUsersRelations = relations(users, ({ many }) => ({
11 | tasks: many(tasks),
12 | labels: many(labels)
13 | }))
14 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/tasks/label.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm'
2 | import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
3 | import { users } from '../auth'
4 | import { taskLabels } from './task-label'
5 |
6 | export const labels = pgTable('label', {
7 | id: uuid('id').defaultRandom().primaryKey(),
8 | name: text('name').notNull(),
9 | createdAt: timestamp('created_at').notNull().defaultNow(),
10 | updatedAt: timestamp('updated_at').notNull().defaultNow(),
11 | userId: text('user_id')
12 | .notNull()
13 | .references(() => users.id)
14 | })
15 |
16 | export const labelsRelations = relations(labels, ({ one, many }) => ({
17 | user: one(users, {
18 | fields: [labels.userId],
19 | references: [users.id]
20 | }),
21 | tasks: many(taskLabels)
22 | }))
23 |
24 | export type Label = typeof labels.$inferSelect
25 | export type NewLabel = typeof labels.$inferInsert
26 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/tasks/task-label.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm'
2 | import { pgTable, uuid } from 'drizzle-orm/pg-core'
3 | import { labels } from './label'
4 | import { tasks } from './task'
5 |
6 | export const taskLabels = pgTable('task_label', {
7 | taskId: uuid('task_id')
8 | .notNull()
9 | .references(() => tasks.id),
10 | labelId: uuid('label_id')
11 | .notNull()
12 | .references(() => labels.id)
13 | })
14 |
15 | export const taskLabelsRelations = relations(taskLabels, ({ one }) => ({
16 | task: one(tasks, {
17 | fields: [taskLabels.taskId],
18 | references: [tasks.id]
19 | }),
20 | label: one(labels, {
21 | fields: [taskLabels.labelId],
22 | references: [labels.id]
23 | })
24 | }))
25 |
--------------------------------------------------------------------------------
/src/core/server/db/schema/tasks/task.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm'
2 | import {
3 | integer,
4 | jsonb,
5 | pgTable,
6 | text,
7 | timestamp,
8 | uuid
9 | } from 'drizzle-orm/pg-core'
10 | import { users } from '../auth'
11 | import { taskLabels } from './task-label'
12 |
13 | export const tasks = pgTable('task', {
14 | id: uuid('id').defaultRandom().primaryKey(),
15 | title: text('title').notNull(),
16 | content: text('content').notNull(),
17 | status: text('status', { enum: ['backlog', 'in-progress', 'completed'] })
18 | .notNull()
19 | .default('backlog'),
20 | dueDate: timestamp('due_date'),
21 | priority: integer('priority'),
22 | createdAt: timestamp('created_at').notNull().defaultNow(),
23 | updatedAt: timestamp('updated_at').notNull().defaultNow(),
24 | userId: text('user_id')
25 | .notNull()
26 | .references(() => users.id),
27 | subtasks: jsonb('subtasks').notNull().default([])
28 | })
29 |
30 | export const tasksRelations = relations(tasks, ({ one, many }) => ({
31 | user: one(users, {
32 | fields: [tasks.userId],
33 | references: [users.id]
34 | }),
35 | labels: many(taskLabels)
36 | }))
37 |
38 | export type Task = typeof tasks.$inferSelect
39 | export type NewTask = typeof tasks.$inferInsert
40 | export type Subtask = { id: string; title: string; completed: boolean }
41 |
--------------------------------------------------------------------------------
/src/core/stores/index.ts:
--------------------------------------------------------------------------------
1 | export * from './store.dismiss-notification'
2 | export * from './store.file-tree'
3 | export * from './store.main.sidebar'
4 | export * from './store.notes'
5 | export * from './store.site-settings'
6 | export * from './store.sub-sidebar'
7 | export * from './store.theme'
8 | export * from './store.toasts'
9 |
--------------------------------------------------------------------------------
/src/core/stores/store.dismiss-notification.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { create } from 'zustand'
4 |
5 | type DismissState = {
6 | isDismissed: boolean
7 | setIsDismissed: (value: boolean) => void
8 | }
9 |
10 | export const useDismissStore = create(set => ({
11 | isDismissed: false,
12 | setIsDismissed: value => set({ isDismissed: value })
13 | }))
14 |
--------------------------------------------------------------------------------
/src/core/stores/store.file-tree.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { TreeItemType } from '@/types/tree-item.types'
4 | import { create } from 'zustand'
5 |
6 | type FileTreeState = {
7 | data: TreeItemType[]
8 | selectedItem: string | null
9 | breadcrumb: string[]
10 | setData: (data: TreeItemType[]) => void
11 | setSelectedItem: (id: string | null) => void
12 | setBreadcrumb: (path: string[]) => void
13 | }
14 |
15 | export const useFileTreeStore = create(set => ({
16 | data: [],
17 | selectedItem: null,
18 | breadcrumb: [],
19 | setData: data => set({ data }),
20 | setSelectedItem: id => set({ selectedItem: id }),
21 | setBreadcrumb: path => set({ breadcrumb: path })
22 | }))
23 |
24 | // Selectors
25 | export const selectFileTreeData = (state: FileTreeState) => state.data
26 | export const selectSelectedItem = (state: FileTreeState) => state.selectedItem
27 | export const selectBreadcrumb = (state: FileTreeState) => state.breadcrumb
28 |
29 | // Actions
30 | export const fileTreeActions = {
31 | setData: (data: TreeItemType[]) =>
32 | useFileTreeStore.getState().setData(data),
33 | setSelectedItem: (id: string | null) =>
34 | useFileTreeStore.getState().setSelectedItem(id),
35 | setBreadcrumb: (path: string[]) =>
36 | useFileTreeStore.getState().setBreadcrumb(path)
37 | }
38 |
--------------------------------------------------------------------------------
/src/core/stores/store.main.sidebar.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | type MainSidebarStore = {
4 | isCollapsed: boolean
5 | toggleCollapse: () => void
6 | }
7 |
8 | export const useMainSidebarStore = create(set => ({
9 | isCollapsed: false,
10 | toggleCollapse: () => set(state => ({ isCollapsed: !state.isCollapsed }))
11 | }))
12 |
--------------------------------------------------------------------------------
/src/core/stores/store.notes.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | interface NotesState {
4 | selectedFolderId: string | null
5 | setSelectedFolderId: (id: string | null) => void
6 | }
7 |
8 | export const useNotesStore = create(set => ({
9 | selectedFolderId: null,
10 | setSelectedFolderId: id => set({ selectedFolderId: id })
11 | }))
12 |
--------------------------------------------------------------------------------
/src/core/stores/store.parsed.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | type ParsedOutput = {
4 | id: string
5 | content: string
6 | createdAt: Date
7 | }
8 |
9 | interface NotesState {
10 | selectedFolderId: string | null
11 | setSelectedFolderId: (id: string | null) => void
12 | parsedOutputs: ParsedOutput[]
13 | addParsedOutput: (content: string) => void
14 | removeParsedOutput: (id: string) => void
15 | }
16 |
17 | export const useNotesStore = create(set => ({
18 | selectedFolderId: null,
19 | setSelectedFolderId: id => set({ selectedFolderId: id }),
20 | parsedOutputs: [],
21 | addParsedOutput: content =>
22 | set(state => ({
23 | parsedOutputs: [
24 | ...state.parsedOutputs,
25 | {
26 | id: Date.now().toString(),
27 | content,
28 | createdAt: new Date()
29 | }
30 | ]
31 | })),
32 | removeParsedOutput: id =>
33 | set(state => ({
34 | parsedOutputs: state.parsedOutputs.filter(
35 | output => output.id !== id
36 | )
37 | }))
38 | }))
39 |
--------------------------------------------------------------------------------
/src/core/stores/store.site-settings.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 | import { persist } from 'zustand/middleware'
3 |
4 | type SiteSettings = {
5 | disableAllAnimations: boolean
6 | disableSidebarAnimations: boolean
7 | toggleAllAnimations: () => void
8 | toggleSidebarAnimations: () => void
9 | }
10 |
11 | export const useSiteSettingsStore = create()(
12 | persist(
13 | set => ({
14 | disableAllAnimations: false,
15 | disableSidebarAnimations: false,
16 | toggleAllAnimations: () =>
17 | set(state => {
18 | const newValue = !state.disableAllAnimations
19 | return {
20 | disableAllAnimations: newValue,
21 | disableSidebarAnimations: newValue
22 | ? true
23 | : state.disableSidebarAnimations
24 | }
25 | }),
26 | toggleSidebarAnimations: () =>
27 | set(state => ({
28 | disableSidebarAnimations: !state.disableSidebarAnimations
29 | }))
30 | }),
31 | {
32 | name: 'site-settings-storage'
33 | }
34 | )
35 | )
36 |
--------------------------------------------------------------------------------
/src/core/stores/store.sub-sidebar.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | type SubSidebarStore = {
4 | isOpen: boolean
5 | toggle: () => void
6 | }
7 |
8 | export const useSubSidebarStore = create(set => ({
9 | isOpen: true,
10 | toggle: () => set(state => ({ isOpen: !state.isOpen }))
11 | }))
12 |
--------------------------------------------------------------------------------
/src/core/stores/store.theme.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 | import { persist } from 'zustand/middleware'
3 |
4 | type Theme = 'default' | 'avocadoAlien' | 'rainbowCandy' | 'honeydewPunch'
5 |
6 | type ThemeStore = {
7 | currentTheme: Theme
8 | setTheme: (theme: Theme) => void
9 | }
10 |
11 | export const useThemeStore = create()(
12 | persist(
13 | set => ({
14 | currentTheme: 'default',
15 | setTheme: theme => set({ currentTheme: theme })
16 | }),
17 | {
18 | name: 'theme-storage'
19 | }
20 | )
21 | )
22 |
--------------------------------------------------------------------------------
/src/core/stores/store.toasts.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | type ToastType = 'error' | 'success' | 'info' | 'warning'
4 |
5 | interface ToastState {
6 | message: string
7 | type: ToastType
8 | isVisible: boolean
9 | showToast: (message: string, type: ToastType) => void
10 | hideToast: () => void
11 | }
12 |
13 | export const toast = create(set => ({
14 | message: '',
15 | type: 'info',
16 | isVisible: false,
17 | showToast: (message: string, type: ToastType) => {
18 | set({ message, type, isVisible: true })
19 | setTimeout(() => {
20 | set({ isVisible: false })
21 | }, 5000)
22 | },
23 | hideToast: () => set({ isVisible: false })
24 | }))
25 |
--------------------------------------------------------------------------------
/src/lib/env.mjs:
--------------------------------------------------------------------------------
1 | import { createEnv } from '@t3-oss/env-nextjs'
2 | import { z } from 'zod'
3 |
4 | export const env = createEnv({
5 | server: {
6 | NODE_ENV: z
7 | .enum(['development', 'test', 'production'])
8 | .default('development'),
9 | DATABASE_URL: z.string().min(1)
10 | },
11 | client: {
12 | // NEXT_PUBLIC_PUBLISHABLE_KEY: z.string().min(1),
13 | },
14 | // If you're using Next.js < 13.4.4, you'll need to specify the runtimeEnv manually
15 | // runtimeEnv: {
16 | // DATABASE_URL: process.env.DATABASE_URL,
17 | // NEXT_PUBLIC_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_PUBLISHABLE_KEY,
18 | // },
19 | // For Next.js >= 13.4.4, you only need to destructure client variables:
20 | experimental__runtimeEnv: {
21 | // NEXT_PUBLIC_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_PUBLISHABLE_KEY,
22 | }
23 | })
24 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from 'clsx'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/src/styles/app.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @import './components/_scrollar.scss';
6 | @import './components/_mouse-grid.scss';
7 | @import './components/_hover-efffect.scss';
8 |
9 | @import './core/_typography.scss';
10 |
11 | @import './themes/_theme-selector.scss';
12 |
13 | @import './utillities/_utillities.scss';
14 | @import './utillities/_border.scss';
15 | @import './utillities/_animations.scss';
16 |
17 | .trans-all {
18 | transition: 500ms ease-in-out all;
19 | }
20 |
21 | @for $i from 1 through 20 {
22 | .trans-#{$i * 100} {
23 | transition: #{$i * 100}ms ease-in-out all;
24 | }
25 | }
26 |
27 | label {
28 | color: var(--text-title);
29 |
30 | &.dark {
31 | color: var(--text-subtitle);
32 | }
33 | }
34 |
35 | dd {
36 | color: var(--text-subtitle);
37 | }
38 |
--------------------------------------------------------------------------------
/src/styles/components/_hover-efffect.scss:
--------------------------------------------------------------------------------
1 | $HOVER_EFFECT_TRANSITION_DURATION: 0.3s;
2 | $HOVER_EFFECT_TRANSITION_TIMING: ease;
3 | $HOVER_EFFECT_TRANSFORM_AMOUNT: -2px;
4 |
5 | $HOVER_EFFECT_GRADIENT_SIZE_OUTER: 350px;
6 | $HOVER_EFFECT_GRADIENT_SIZE_INNER: 350px;
7 | $HOVER_EFFECT_GRADIENT_OPACITY_OUTER: 0.03;
8 | $HOVER_EFFECT_GRADIENT_OPACITY_INNER: 0.05;
9 | $HOVER_EFFECT_GRADIENT_SPREAD: 50%;
10 |
11 | .hover-effect {
12 | position: relative;
13 | transition: transform $HOVER_EFFECT_TRANSITION_DURATION
14 | $HOVER_EFFECT_TRANSITION_TIMING;
15 |
16 | &::before,
17 | &::after {
18 | content: '';
19 | position: absolute;
20 | top: 0;
21 | left: 0;
22 | right: 0;
23 | bottom: 0;
24 | border-radius: inherit;
25 | pointer-events: none;
26 | opacity: 0;
27 | transition: opacity $HOVER_EFFECT_TRANSITION_DURATION;
28 | }
29 |
30 | &::before {
31 | background: radial-gradient(
32 | #{$HOVER_EFFECT_GRADIENT_SIZE_OUTER} circle at var(--mouse-x) var(--mouse-y),
33 | rgba(137, 119, 125, $HOVER_EFFECT_GRADIENT_OPACITY_OUTER),
34 | transparent $HOVER_EFFECT_GRADIENT_SPREAD
35 | );
36 | z-index: 1;
37 | }
38 |
39 | &::after {
40 | background: radial-gradient(
41 | #{$HOVER_EFFECT_GRADIENT_SIZE_INNER} circle at var(--mouse-x) var(--mouse-y),
42 | rgba(137, 119, 125, $HOVER_EFFECT_GRADIENT_OPACITY_INNER),
43 | transparent $HOVER_EFFECT_GRADIENT_SPREAD
44 | );
45 | z-index: 2;
46 | }
47 |
48 | &:hover {
49 | transform: translateY($HOVER_EFFECT_TRANSFORM_AMOUNT);
50 |
51 | &::before,
52 | &::after {
53 | opacity: 1;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/styles/components/_mouse-grid.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --bg-color: rgb(20, 20, 20);
3 | --card-color: rgb(23, 23, 23);
4 | }
5 |
6 | .card::before,
7 | .card::after {
8 | border-radius: inherit;
9 | content: none;
10 | height: 100%;
11 | left: 0;
12 | opacity: 0;
13 | position: absolute;
14 | top: 0;
15 | transition: opacity 0.5s;
16 | width: 100%;
17 | }
18 |
19 | .card::before {
20 | background: radial-gradient(
21 | 800px circle at var(--mouse-x) var(--mouse-y),
22 | rgba(255, 255, 255, 0.06),
23 | transparent 40%
24 | );
25 | z-index: 3;
26 | }
27 |
28 | .card::after {
29 | background: radial-gradient(
30 | 600px circle at var(--mouse-x) var(--mouse-y),
31 | rgba(255, 255, 255, 0.4),
32 | transparent 40%
33 | );
34 | z-index: 1;
35 | }
36 |
37 | #cards:hover > .card::after {
38 | opacity: 100;
39 | }
40 |
--------------------------------------------------------------------------------
/src/styles/components/_scrollar.scss:
--------------------------------------------------------------------------------
1 | /* Styling for Webkit browsers (Chrome, Safari, newer versions of Edge) */
2 | ::-webkit-scrollbar {
3 | width: 6px;
4 | /* Even thinner width */
5 | }
6 |
7 | ::-webkit-scrollbar-track {
8 | background: transparent;
9 | /* Transparent track */
10 | }
11 |
12 | ::-webkit-scrollbar-thumb {
13 | background: rgba(255, 255, 255, 0.1);
14 | /* Very faint light color */
15 | border-radius: 3px;
16 | /* Slightly rounded corners */
17 | }
18 |
19 | ::-webkit-scrollbar-thumb:hover {
20 | background: rgba(255, 255, 255, 0.2);
21 | /* Slightly more visible on hover */
22 | }
23 |
24 | /* Styling for Firefox */
25 | * {
26 | scrollbar-width: thin;
27 | scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
28 | }
29 |
30 | /* Hide scrollbar when not in use (optional) */
31 | ::-webkit-scrollbar-thumb {
32 | visibility: hidden;
33 | }
34 |
35 | :hover::-webkit-scrollbar-thumb {
36 | visibility: visible;
37 | }
38 |
--------------------------------------------------------------------------------
/src/styles/core/_typography.scss:
--------------------------------------------------------------------------------
1 | h1,
2 | h2,
3 | h3,
4 | h4,
5 | h5,
6 | h6,
7 | p {
8 | color: var(--text-title);
9 |
10 | &.subtitle {
11 | color: var(--text-subtitle) !important;
12 | }
13 | }
14 |
15 | /* Indeed, the tag does not exists, it's nota typo but I wanted a span value to have this color, but can't globally change the color of all spans so I opted to crate a new element, also beacuse I didn't knew this was possible. */
16 | spa {
17 | color: var(--text-subtitle);
18 | }
19 |
20 | title,
21 | ptitle {
22 | color: var(--text-title);
23 | }
24 |
25 | subtitle,
26 | psub {
27 | color: var(--text-subtitle);
28 | }
29 |
30 | .subtitle {
31 | color: var(--text-subtitle);
32 | }
33 |
34 | .title {
35 | color: var(--text-title);
36 | }
37 |
38 | .heading {
39 | background-image: linear-gradient(
40 | to bottom,
41 | var(--text-title),
42 | var(--white--64)
43 | );
44 | -webkit-background-clip: text;
45 | background-clip: text;
46 | -webkit-text-fill-color: transparent;
47 | }
48 |
--------------------------------------------------------------------------------
/src/styles/themes/_theme-selector.scss:
--------------------------------------------------------------------------------
1 | @import './default-theme'; // default theme
2 |
--------------------------------------------------------------------------------
/src/styles/utillities/_utillities.scss:
--------------------------------------------------------------------------------
1 | .unset-all {
2 | all: unset;
3 | }
4 |
5 | .shadow-left,
6 | .shadow-right {
7 | position: absolute;
8 | top: 10%;
9 | bottom: 0;
10 | width: 45%;
11 | pointer-events: none;
12 | z-index: 1;
13 | }
14 |
15 | .shadow-left {
16 | left: 0;
17 | background: linear-gradient(to right, var(--base--background), transparent);
18 | }
19 |
20 | .shadow-right {
21 | right: 0;
22 | background: linear-gradient(to left, var(--base--background), transparent);
23 | }
24 |
25 | .footer-rainbow {
26 | transform: translate(-40px, -49px);
27 | }
28 |
29 | li:marker {
30 | display: none !important;
31 | }
32 |
--------------------------------------------------------------------------------
/src/types/canvas-confetti.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'canvas-confetti' {
2 | function confetti(options?: any): Promise
3 |
4 | namespace confetti {
5 | function create(
6 | canvas: HTMLCanvasElement,
7 | options?: any
8 | ): {
9 | confetti: typeof confetti
10 | reset: () => void
11 | }
12 | }
13 |
14 | export = confetti
15 | }
16 |
--------------------------------------------------------------------------------
/src/types/design-system.d.ts:
--------------------------------------------------------------------------------
1 | export type DesignSystemWrapperProps = {
2 | title: string
3 | description?: string
4 | actionButtons?: { label: string; onClick: () => void }[]
5 | children: React.ReactNode
6 | }
7 |
--------------------------------------------------------------------------------
/src/types/global.d.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 |
3 | declare global {
4 | type PageProps = {
5 | children?: ReactNode
6 | }
7 | type ChildrenProps = {
8 | children?: ReactNode
9 | }
10 | }
11 |
12 | // It's important to include this line to make it a module
13 | export {}
14 |
--------------------------------------------------------------------------------
/src/types/tasks.d.ts:
--------------------------------------------------------------------------------
1 | export type TaskStatus = 'backlog' | 'in-progress' | 'completed'
2 | export type TaskPriority = 1 | 2 | 3
3 |
4 | export type Subtask = {
5 | id: string
6 | title: string
7 | completed: boolean
8 | }
9 |
10 | export type Comment = {
11 | id: string
12 | content: string
13 | createdAt: string
14 | userId: string
15 | }
16 |
17 | export type Task = {
18 | id: string
19 | title: string
20 | content: string
21 | status: TaskStatus
22 | priority: TaskPriority
23 | labels: string[]
24 | dueDate: string | null
25 | estimatedTime: number | null
26 | actualTime: number | null
27 | assignee: string | null
28 | subtasks: Subtask[]
29 | comments: Comment[]
30 | createdAt: string
31 | updatedAt: string
32 | dependencies: string[]
33 | }
34 |
35 | export type NewTask = {
36 | subtasks: any[]
37 | title: string
38 | content: string
39 | status: TaskStatus
40 | priority: TaskPriority
41 | labels: string[]
42 | dueDate: string | null
43 | estimatedTime: number | null
44 | }
45 |
--------------------------------------------------------------------------------
/src/types/tree-item.types.d.ts:
--------------------------------------------------------------------------------
1 | export type TreeItemType = {
2 | id: string
3 | name: string
4 | type: 'folder' | 'file'
5 | children?: TreeItemType[]
6 | }
7 |
--------------------------------------------------------------------------------
/src/types/types.folder.d.ts:
--------------------------------------------------------------------------------
1 | export type FolderType = {
2 | id: string
3 | name: string
4 | description: string | null
5 | color: string
6 | children?: FolderType[]
7 | parentId?: string | null
8 | }
9 |
--------------------------------------------------------------------------------
/src/types/types.users.d.ts:
--------------------------------------------------------------------------------
1 | export type ActionResult = {
2 | error: string | null
3 | success: boolean
4 | message?: string
5 | data?: Record
6 | }
7 |
8 | export type UserProfile = {
9 | userId?: string
10 | firstName?: string
11 | lastName?: string
12 | username?: string
13 | email?: string
14 | dateOfBirth?: string
15 | occupation?: string
16 | bio?: string
17 | github?: string
18 | linkedin?: string
19 | twitter?: string
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "strictNullChecks": false,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "node",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"],
23 | "models": ["./src/core/models/index.ts"],
24 | "toast": ["./src/core/stores/store.toasts.ts"],
25 | "cn": ["src/core/constants/cn-tw.ts"],
26 | "locales": ["./src/core/config/locales/locales.ts"],
27 | "db": ["./src/core/server/db/index.ts"],
28 | "hooks": ["./src/core/hooks/index.ts"],
29 | "schema": ["./src/core/server/schema/index.ts"],
30 | "actions": ["./src/core/server/actions/index.ts"],
31 | "ui": ["./src/components/ui/index.ts"],
32 | "stores": ["./src/core/stores/index.ts"],
33 | "atoms": ["./src/components/atoms/index.ts"]
34 | },
35 | "target": "esnext",
36 | "baseUrl": "./"
37 | },
38 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
39 | "exclude": ["node_modules"]
40 | }
41 |
--------------------------------------------------------------------------------