├── src ├── constants │ └── index.ts ├── components │ ├── minimal-tiptap │ │ ├── index.ts │ │ ├── extensions │ │ │ ├── color │ │ │ │ ├── index.ts │ │ │ │ └── color.ts │ │ │ ├── image │ │ │ │ ├── index.ts │ │ │ │ ├── components │ │ │ │ │ ├── image-overlay.tsx │ │ │ │ │ └── resize-handle.tsx │ │ │ │ └── hooks │ │ │ │ │ └── use-image-actions.ts │ │ │ ├── link │ │ │ │ ├── index.ts │ │ │ │ └── link.ts │ │ │ ├── selection │ │ │ │ ├── index.ts │ │ │ │ └── selection.ts │ │ │ ├── horizontal-rule │ │ │ │ ├── index.ts │ │ │ │ └── horizontal-rule.ts │ │ │ ├── unset-all-marks │ │ │ │ ├── index.ts │ │ │ │ └── unset-all-marks.ts │ │ │ ├── code-block-lowlight │ │ │ │ ├── index.ts │ │ │ │ └── code-block-lowlight.ts │ │ │ ├── reset-marks-on-enter │ │ │ │ ├── index.ts │ │ │ │ └── reset-marks-on-enter.ts │ │ │ ├── index.ts │ │ │ └── file-handler │ │ │ │ └── index.ts │ │ ├── styles │ │ │ └── partials │ │ │ │ ├── placeholder.css │ │ │ │ ├── lists.css │ │ │ │ ├── typography.css │ │ │ │ ├── code.css │ │ │ │ └── zoom.css │ │ ├── types.ts │ │ ├── hooks │ │ │ ├── use-theme.ts │ │ │ └── use-throttle.ts │ │ ├── components │ │ │ ├── spinner.tsx │ │ │ ├── shortcut-key.tsx │ │ │ ├── toolbar-button.tsx │ │ │ ├── image │ │ │ │ ├── image-edit-dialog.tsx │ │ │ │ └── image-edit-block.tsx │ │ │ ├── link │ │ │ │ ├── link-edit-popover.tsx │ │ │ │ ├── link-popover-block.tsx │ │ │ │ └── link-edit-block.tsx │ │ │ ├── section │ │ │ │ ├── four.tsx │ │ │ │ ├── five.tsx │ │ │ │ └── two.tsx │ │ │ └── bubble-menu │ │ │ │ └── link-bubble-menu.tsx │ │ └── minimal-tiptap.tsx │ ├── ui │ │ ├── aspect-ratio.tsx │ │ ├── skeleton.tsx │ │ ├── collapsible.tsx │ │ ├── label.tsx │ │ ├── textarea.tsx │ │ ├── separator.tsx │ │ ├── progress.tsx │ │ ├── toaster.tsx │ │ ├── input.tsx │ │ ├── sonner.tsx │ │ ├── checkbox.tsx │ │ ├── slider.tsx │ │ ├── switch.tsx │ │ ├── badge.tsx │ │ ├── tooltip.tsx │ │ ├── hover-card.tsx │ │ ├── popover.tsx │ │ ├── avatar.tsx │ │ ├── toggle.tsx │ │ ├── radio-group.tsx │ │ ├── alert.tsx │ │ ├── scroll-area.tsx │ │ ├── resizable.tsx │ │ ├── toggle-group.tsx │ │ ├── tabs.tsx │ │ ├── card.tsx │ │ ├── accordion.tsx │ │ ├── button.tsx │ │ ├── input-otp.tsx │ │ ├── calendar.tsx │ │ ├── breadcrumb.tsx │ │ ├── pagination.tsx │ │ ├── table.tsx │ │ └── drawer.tsx │ ├── date-range-picker.tsx │ ├── column-selection.tsx │ ├── combo-box.tsx │ └── date-picker.tsx ├── app │ ├── favicon.ico │ ├── page.tsx │ ├── api │ │ └── [[...route]] │ │ │ ├── route.ts │ │ │ ├── user-profiles.ts │ │ │ └── orders.ts │ ├── tasks │ │ ├── [id] │ │ │ └── page.tsx │ │ └── page.tsx │ ├── layout.tsx │ └── globals.css ├── lib │ ├── utils.ts │ └── hono.ts ├── database │ ├── drizzle.ts │ ├── schema.ts │ └── schemas │ │ └── schema-helper.ts ├── hooks │ ├── use-mobile.tsx │ └── use-confirm.tsx ├── features │ └── tasks │ │ ├── schema │ │ └── add-task.schema.ts │ │ ├── types │ │ ├── index.ts │ │ └── filters.ts │ │ ├── hooks │ │ ├── use-new-task.ts │ │ └── use-edit-task.ts │ │ ├── components │ │ ├── task-type.tsx │ │ ├── add-task │ │ │ └── new-task-sheet.tsx │ │ ├── task-form-skeleton.tsx │ │ ├── update-task │ │ │ └── update-task-sheet.tsx │ │ ├── task-form │ │ │ └── form-fields │ │ │ │ ├── date-field.tsx │ │ │ │ ├── number-field.tsx │ │ │ │ ├── description-field.tsx │ │ │ │ └── title-field.tsx │ │ ├── task-status.tsx │ │ ├── task-search.tsx │ │ ├── task-priority.tsx │ │ └── task-actions.tsx │ │ ├── queries │ │ └── user-profiles.queries.ts │ │ └── index.tsx ├── providers │ ├── sheet.provider.tsx │ └── query.provider.tsx └── stores │ └── auth-store.ts ├── .prettierrc ├── bun.lockb ├── public ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── .env-sample ├── postcss.config.mjs ├── next.config.ts ├── .eslintrc.json ├── drizzle.config.ts ├── drizzle ├── meta │ └── _journal.json ├── 0001_charming_pepper_potts.sql └── 0000_first_dark_phoenix.sql ├── components.json ├── .gitignore ├── tsconfig.json ├── license.md ├── tailwind.config.ts ├── README.md └── package.json /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PAGE_SIZE = 15; -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } -------------------------------------------------------------------------------- /src/components/minimal-tiptap/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./minimal-tiptap"; 2 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KaraBharat/Task-List-Demo/HEAD/bun.lockb -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/color/index.ts: -------------------------------------------------------------------------------- 1 | export * from './color' 2 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/image/index.ts: -------------------------------------------------------------------------------- 1 | export * from './image' 2 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/link/index.ts: -------------------------------------------------------------------------------- 1 | export * from './link' 2 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/selection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './selection' 2 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KaraBharat/Task-List-Demo/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/horizontal-rule/index.ts: -------------------------------------------------------------------------------- 1 | export * from './horizontal-rule' 2 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/unset-all-marks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './unset-all-marks' 2 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/code-block-lowlight/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code-block-lowlight' 2 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/reset-marks-on-enter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './reset-marks-on-enter' 2 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env-sample: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_APP_URL=http://localhost:3000/ 2 | DATABASE_URL='postgresql://xxxxxxx-xxxxxx:xxxxxxx-xxxxxx@xxxxxxx-xxxxxx.db.neon.tech/xxxxxxx-xxxxxx?sslmode=require' -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /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/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/styles/partials/placeholder.css: -------------------------------------------------------------------------------- 1 | .minimal-tiptap-editor .ProseMirror > p.is-editor-empty::before { 2 | content: attr(data-placeholder); 3 | @apply pointer-events-none float-left h-0 text-[var(--mt-secondary)]; 4 | } 5 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | images: { 6 | remotePatterns: [ 7 | { hostname: "images.unsplash.com", protocol: "https" }, 8 | ], 9 | }, 10 | }; 11 | 12 | export default nextConfig; 13 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/unset-all-marks/unset-all-marks.ts: -------------------------------------------------------------------------------- 1 | import { Extension } from '@tiptap/core' 2 | 3 | export const UnsetAllMarks = Extension.create({ 4 | addKeyboardShortcuts() { 5 | return { 6 | 'Mod-\\': () => this.editor.commands.unsetAllMarks() 7 | } 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code-block-lowlight' 2 | export * from './color' 3 | export * from './horizontal-rule' 4 | export * from './image' 5 | export * from './link' 6 | export * from './selection' 7 | export * from './unset-all-marks' 8 | export * from './reset-marks-on-enter' 9 | export * from './file-handler' 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"], 3 | "rules": { 4 | "@typescript-eslint/no-empty-interface": 0, 5 | "@typescript-eslint/no-unused-vars": 0, 6 | "@typescript-eslint/no-empty-object-type": 0, 7 | "@typescript-eslint/no-explicit-any": 0, 8 | "@typescript-eslint/no-unused-expressions": 0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from "dotenv"; 2 | import { defineConfig } from "drizzle-kit"; 3 | 4 | config({ path: ".env.local" }); 5 | 6 | export default defineConfig({ 7 | schema: "./src/database/schemas/*.schema.ts", 8 | dialect: "postgresql", 9 | dbCredentials: { 10 | url: process.env.DATABASE_URL!, 11 | }, 12 | verbose: true, 13 | strict: true, 14 | }); -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "7", 8 | "when": 1730730750035, 9 | "tag": "0000_first_dark_phoenix", 10 | "breakpoints": true 11 | }, 12 | { 13 | "idx": 1, 14 | "version": "7", 15 | "when": 1740989793174, 16 | "tag": "0001_charming_pepper_potts", 17 | "breakpoints": true 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /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": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } -------------------------------------------------------------------------------- /src/database/drizzle.ts: -------------------------------------------------------------------------------- 1 | import { neon } from "@neondatabase/serverless"; 2 | import { drizzle } from "drizzle-orm/neon-http"; 3 | import * as orderSchema from "./schemas/order.schema"; 4 | import * as taskSchema from "./schemas/task.schema"; 5 | 6 | /** 7 | * Database Connection Setup 8 | */ 9 | 10 | // Initialize the Neon database connection 11 | export const sql = neon(process.env.DATABASE_URL!); 12 | 13 | // Create a Drizzle ORM instance 14 | export const db = drizzle(sql, { schema: { ...orderSchema, ...taskSchema } }); 15 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/styles/partials/lists.css: -------------------------------------------------------------------------------- 1 | .minimal-tiptap-editor .ProseMirror ol { 2 | @apply list-decimal; 3 | } 4 | 5 | .minimal-tiptap-editor .ProseMirror ol ol { 6 | list-style: lower-alpha; 7 | } 8 | 9 | .minimal-tiptap-editor .ProseMirror ol ol ol { 10 | list-style: lower-roman; 11 | } 12 | 13 | .minimal-tiptap-editor .ProseMirror ul { 14 | list-style: disc; 15 | } 16 | 17 | .minimal-tiptap-editor .ProseMirror ul ul { 18 | list-style: circle; 19 | } 20 | 21 | .minimal-tiptap-editor .ProseMirror ul ul ul { 22 | list-style: square; 23 | } 24 | -------------------------------------------------------------------------------- /src/database/schema.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Database Schema Configuration 3 | * Combines all individual schema definitions into a single export 4 | * for database initialization and type definitions 5 | */ 6 | 7 | // Internal Schema Imports 8 | import * as taskSchema from "./schemas/task.schema"; 9 | import * as orderSchema from "./schemas/order.schema"; 10 | 11 | /** 12 | * Combined schema object containing all database schemas 13 | 14 | * Used for database initialization and type definitions 15 | */ 16 | export const schema = { 17 | ...taskSchema, 18 | ...orderSchema, 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/hono.ts: -------------------------------------------------------------------------------- 1 | import { AppType } from "@/app/api/[[...route]]/route"; 2 | import { hc } from "hono/client"; 3 | 4 | /** 5 | * Hono client configuration 6 | * This client is used for making API requests to the application's backend. 7 | */ 8 | 9 | /** 10 | * Create a typed Hono client instance 11 | * @type {ReturnType>} 12 | */ 13 | export const client = hc(process.env.NEXT_PUBLIC_APP_URL!); 14 | 15 | // Ensure the environment variable is set 16 | if (!process.env.NEXT_PUBLIC_APP_URL) { 17 | console.warn("API URL is not set. API requests may fail."); 18 | } 19 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/code-block-lowlight/code-block-lowlight.ts: -------------------------------------------------------------------------------- 1 | import { CodeBlockLowlight as TiptapCodeBlockLowlight } from '@tiptap/extension-code-block-lowlight' 2 | import { common, createLowlight } from 'lowlight' 3 | 4 | export const CodeBlockLowlight = TiptapCodeBlockLowlight.extend({ 5 | addOptions() { 6 | return { 7 | ...this.parent?.(), 8 | lowlight: createLowlight(common), 9 | defaultLanguage: null, 10 | HTMLAttributes: { 11 | class: 'block-node' 12 | } 13 | } 14 | } 15 | }) 16 | 17 | export default CodeBlockLowlight 18 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/horizontal-rule/horizontal-rule.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Wrap the horizontal rule in a div element. 3 | * Also add a keyboard shortcut to insert a horizontal rule. 4 | */ 5 | import { HorizontalRule as TiptapHorizontalRule } from '@tiptap/extension-horizontal-rule' 6 | 7 | export const HorizontalRule = TiptapHorizontalRule.extend({ 8 | addKeyboardShortcuts() { 9 | return { 10 | 'Mod-Alt--': () => 11 | this.editor.commands.insertContent({ 12 | type: this.name 13 | }) 14 | } 15 | } 16 | }) 17 | 18 | export default HorizontalRule 19 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/image/components/image-overlay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Spinner } from '../../../components/spinner' 3 | import { cn } from '@/lib/utils' 4 | 5 | export const ImageOverlay = React.memo(() => { 6 | return ( 7 |
13 | 14 |
15 | ) 16 | }) 17 | 18 | ImageOverlay.displayName = 'ImageOverlay' -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/color/color.ts: -------------------------------------------------------------------------------- 1 | import { Color as TiptapColor } from '@tiptap/extension-color' 2 | import { Plugin } from '@tiptap/pm/state' 3 | 4 | export const Color = TiptapColor.extend({ 5 | addProseMirrorPlugins() { 6 | return [ 7 | ...(this.parent?.() || []), 8 | new Plugin({ 9 | props: { 10 | handleKeyDown: (_, event) => { 11 | if (event.key === 'Enter') { 12 | this.editor.commands.unsetColor() 13 | } 14 | return false 15 | } 16 | } 17 | }) 18 | ] 19 | } 20 | }) 21 | -------------------------------------------------------------------------------- /.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # env files (can opt-in for commiting if needed) 33 | .env.local 34 | .env 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /src/hooks/use-mobile.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | const MOBILE_BREAKPOINT = 768 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState(undefined) 7 | 8 | React.useEffect(() => { 9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) 10 | const onChange = () => { 11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 12 | } 13 | mql.addEventListener("change", onChange) 14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 15 | return () => mql.removeEventListener("change", onChange) 16 | }, []) 17 | 18 | return !!isMobile 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 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 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/extensions/reset-marks-on-enter/reset-marks-on-enter.ts: -------------------------------------------------------------------------------- 1 | import { Extension } from '@tiptap/core' 2 | 3 | export const ResetMarksOnEnter = Extension.create({ 4 | name: 'resetMarksOnEnter', 5 | 6 | addKeyboardShortcuts() { 7 | return { 8 | Enter: ({ editor }) => { 9 | if ( 10 | editor.isActive('bold') || 11 | editor.isActive('italic') || 12 | editor.isActive('strike') || 13 | editor.isActive('underline') || 14 | editor.isActive('code') 15 | ) { 16 | editor.commands.splitBlock({ keepMarks: false }) 17 | 18 | return true 19 | } 20 | 21 | return false 22 | } 23 | } 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/types.ts: -------------------------------------------------------------------------------- 1 | import type { Editor } from '@tiptap/core' 2 | import type { EditorView } from '@tiptap/pm/view' 3 | import type { EditorState } from '@tiptap/pm/state' 4 | 5 | export interface LinkProps { 6 | url: string 7 | text?: string 8 | openInNewTab?: boolean 9 | } 10 | 11 | export interface ShouldShowProps { 12 | editor: Editor 13 | view: EditorView 14 | state: EditorState 15 | oldState?: EditorState 16 | from: number 17 | to: number 18 | } 19 | 20 | export interface FormatAction { 21 | label: string 22 | icon?: React.ReactNode 23 | action: (editor: Editor) => void 24 | isActive: (editor: Editor) => boolean 25 | canExecute: (editor: Editor) => boolean 26 | shortcuts: string[] 27 | value: string 28 | } 29 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/hooks/use-theme.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export const useTheme = () => { 4 | const [isDarkMode, setIsDarkMode] = React.useState(false) 5 | 6 | React.useEffect(() => { 7 | const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') 8 | setIsDarkMode(darkModeMediaQuery.matches) 9 | 10 | const handleChange = (e: MediaQueryListEvent) => { 11 | const newDarkMode = e.matches 12 | setIsDarkMode(newDarkMode) 13 | } 14 | 15 | darkModeMediaQuery.addEventListener('change', handleChange) 16 | 17 | return () => { 18 | darkModeMediaQuery.removeEventListener('change', handleChange) 19 | } 20 | }, []) 21 | 22 | return isDarkMode 23 | } 24 | 25 | export default useTheme 26 | -------------------------------------------------------------------------------- /src/features/tasks/schema/add-task.schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | import { taskPriorities, taskStatuses, taskTypes } from "@/database/schemas/task.schema"; 3 | 4 | export const taskSchema = z.object({ 5 | title: z.string().min(1, "Title is required"), 6 | description: z.string().optional(), 7 | status: z.enum(taskStatuses.enumValues), 8 | dueDate: z.string().optional(), 9 | assigneeId: z.string().min(1, "Assignee is required"), 10 | priority: z.enum(taskPriorities.enumValues).optional(), 11 | type: z.enum(taskTypes.enumValues).optional(), 12 | storyPoints: z.number().min(0).optional(), 13 | timeEstimate: z.number().min(0).optional(), 14 | timeSpent: z.number().min(0).optional(), 15 | }); 16 | 17 | export type TaskFormData = z.infer; 18 | -------------------------------------------------------------------------------- /src/components/minimal-tiptap/styles/partials/typography.css: -------------------------------------------------------------------------------- 1 | .minimal-tiptap-editor .ProseMirror .heading-node { 2 | @apply relative font-semibold; 3 | } 4 | 5 | .minimal-tiptap-editor .ProseMirror .heading-node:first-child { 6 | @apply mt-0; 7 | } 8 | 9 | .minimal-tiptap-editor .ProseMirror h1 { 10 | @apply mb-4 mt-[46px] text-[1.375rem] leading-7 tracking-[-0.004375rem]; 11 | } 12 | 13 | .minimal-tiptap-editor .ProseMirror h2 { 14 | @apply mb-3.5 mt-8 text-[1.1875rem] leading-7 tracking-[0.003125rem]; 15 | } 16 | 17 | .minimal-tiptap-editor .ProseMirror h3 { 18 | @apply mb-3 mt-6 text-[1.0625rem] leading-6 tracking-[0.00625rem]; 19 | } 20 | 21 | .minimal-tiptap-editor .ProseMirror a.link { 22 | @apply cursor-pointer text-primary; 23 | } 24 | 25 | .minimal-tiptap-editor .ProseMirror a.link:hover { 26 | @apply underline; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 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/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |