├── .env.example ├── .eslintrc.cjs ├── .gitignore ├── LICENSE ├── README.md ├── SETUP-GUIDE.md ├── bun.lockb ├── components.json ├── next.config.js ├── package.json ├── postcss.config.cjs ├── prettier.config.js ├── prisma └── schema.prisma ├── public ├── TymelyManage.jpeg ├── TymelyManageLight.jpeg ├── TymelyPreview.jpeg ├── TymelyPreviewLight.jpeg ├── TymelyTimeBlocker.png ├── TymelyTimeBlockerLight.png ├── TymelyWorkflow.jpeg ├── TymelyWorkflowLight.jpeg ├── favicon.ico └── tymely-logo.png ├── src ├── Provider │ ├── modal-provider.tsx │ ├── theme-provider.tsx │ └── toast-provider.tsx ├── app │ ├── (pages) │ │ └── workflows │ │ │ ├── [id] │ │ │ ├── board │ │ │ │ └── page.tsx │ │ │ ├── calendar │ │ │ │ └── page.tsx │ │ │ ├── flow │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ │ └── page.tsx │ ├── api │ │ └── auth │ │ │ └── [...nextauth] │ │ │ └── route.ts │ ├── layout.tsx │ └── page.tsx ├── components │ ├── Bento.tsx │ ├── forms │ │ ├── flow-form.tsx │ │ └── workflow-form.tsx │ ├── global │ │ ├── LoginButton.tsx │ │ ├── ModeToggle.tsx │ │ ├── Navbar.tsx │ │ ├── custom-modal.tsx │ │ ├── floating-nav.tsx │ │ ├── lampComponent.tsx │ │ ├── radical-gradient.tsx │ │ ├── sparkles.tsx │ │ ├── theme-image-changer.tsx │ │ └── useravatar.tsx │ ├── magic-card.tsx │ ├── ui │ │ ├── animated-bean.tsx │ │ ├── animated-gradient-text.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── bento-grid.tsx │ │ ├── box-reveal.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── number-ticker.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── sheet.tsx │ │ ├── shimmer-border-beam.tsx │ │ ├── shimmer-button.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── tooltip.tsx │ │ └── words-pullup.tsx │ ├── workflow │ │ ├── Calendar.tsx │ │ ├── WorkflowNavbar.tsx │ │ └── milestoneEdit.tsx │ ├── workflowButton.tsx │ └── workflowCard.tsx ├── env.js ├── hooks │ └── useClientMediaQuery.ts ├── lib │ ├── types.ts │ └── utils.ts ├── server │ ├── actions.ts │ ├── auth.ts │ ├── config.ts │ └── db.ts └── styles │ └── globals.css ├── start-database.sh ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="database - postgresql" 8 | # uncomment next line if you use Prisma <5.10 9 | # DATABASE_URL_UNPOOLED="postgresql://tymelydb_owner:avSHFRtgD2d7@ep-rough-dust-a5608beq.us-east-2.aws.neon.tech/tymelydb?sslmode=require" 10 | NEXTAUTH_URL="https://localhost:3000" 11 | 12 | NEXTAUTH_SECRET="ReallySecretyStuffHere" 13 | 14 | #Uploadthing 15 | 16 | # Next Auth Google Provider 17 | GOOGLE_CLIENT_ID="Your ID" 18 | GOOGLE_CLIENT_SECRET="Your Secret" 19 | GEMINI_API_KEY="somethinghere" 20 | Prompt="Whatever you want" 21 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | const config = { 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "project": true 6 | }, 7 | "plugins": [ 8 | "@typescript-eslint" 9 | ], 10 | "extends": [ 11 | "next/core-web-vitals", 12 | "plugin:@typescript-eslint/recommended-type-checked", 13 | "plugin:@typescript-eslint/stylistic-type-checked" 14 | ], 15 | "rules": { 16 | "@typescript-eslint/array-type": "off", 17 | "@typescript-eslint/consistent-type-definitions": "off", 18 | "@typescript-eslint/consistent-type-imports": [ 19 | "warn", 20 | { 21 | "prefer": "type-imports", 22 | "fixStyle": "inline-type-imports" 23 | } 24 | ], 25 | "@typescript-eslint/no-unused-vars": [ 26 | "warn", 27 | { 28 | "argsIgnorePattern": "^_" 29 | } 30 | ], 31 | "@typescript-eslint/require-await": "off", 32 | "@typescript-eslint/no-misused-promises": [ 33 | "error", 34 | { 35 | "checksVoidReturn": { 36 | "attributes": false 37 | } 38 | } 39 | ] 40 | } 41 | } 42 | module.exports = config; -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | /coverage 10 | 11 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | db.sqlite 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | next-env.d.ts 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | .pnpm-debug.log* 33 | 34 | # local env files 35 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 36 | .env 37 | .env*.local 38 | 39 | # vercel 40 | .vercel 41 | 42 | # typescript 43 | *.tsbuildinfo 44 | 45 | certificates -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Om Shah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![og image](https://tymely.vercel.app/tymely-logo.png) 2 | 3 | # Tymely 4 | 5 | Interested in helping build cool apps! Help me make this more awesome! 6 | Start by following me on [X](https://x.com/MaiOmmHoon/status/1795338096902574450) where I will keep you updated! If you have any idea just make a new Issue and we will talk there! 7 | 8 | ## 👀 What is this? 9 | 10 | Tymely is an AI powered planner that can help you to create you own plan for your next idea/goal! Ever got stuck while learning by yourself and wished if there was a path/roadmap! I got you bro! 11 | For now it's just a small app but I'll add features that will help you. 12 | 13 | 14 | ## How do I use this? 15 | 16 | Just go to [Tymely.vercel.app](https://tymely.vercel.app) and sign in with your google account. 17 | 1. Go to the workflow page to start with your goal 18 | 2. Add a new workflow where you will define your deadline and your goal. 19 | [Workflow drawer](https://tymely.vercel.app/TymelyWorkflow.jpeg) 20 | 21 | That's ALL 22 | 23 | ## Stack 24 | - [Nextjs 14](https://nextjs.org/) 25 | - [T3-stack](https://create.t3.gg/) 26 | - [Next Auth](https://next-auth.js.org/) 27 | - [TailwindCSS](https://tailwindcss.com) 28 | - [shadcn-ui](https://ui.shadcn.com) 29 | - [MagicUI](https://magicui.design/) 30 | - [AceternityUI](https://ui.aceternity.com/) 31 | - [Gemini API](https://ai.google.dev/) 32 | - and many more [will update soon] 33 | 34 | ### Contributing 35 | 36 | Contributions are very welcome! A contribution can be as small as a ⭐ or even finding and creating issues. 37 | 38 | ### SetUP 39 | 40 | Please see the [SETUP-GUIDE.md](SETUP-GUIDE.md) for setup instructions. 41 | 42 | -------------------------------------------------------------------------------- /SETUP-GUIDE.md: -------------------------------------------------------------------------------- 1 | # Setup guide 2 | 3 | ## Prerequisites 4 | 5 | - [bun](https://bun.sh/) 6 | 7 | ## Steps 8 | 9 | 1. Clone the repo 10 | 2. Run `bun install` in the root directory 11 | 12 | ```bash 13 | GOOGLE_CLIENT_ID="-" 14 | GOOGLE_CLIENT_SECRET="-" 15 | GEMINI_API_KEY="GeminiAPI - use Gemini pro" 16 | Prompt="Whatever you want" 17 | NEXTAUTH_SECRET='nextauthsecret' 18 | DATABASE_URL='database.postgresql' 19 | NEXTAUTH_URL='http://localhost:3000' 20 | ``` 21 | 22 | 8. Run `bun dev` in the root directory and Voila! You have your own tymely instance running! 23 | 24 | ### THAT'S ALL!! -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/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/styles/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | // await import("./src/env.js"); 6 | 7 | /** @type {import("next").NextConfig} */ 8 | const config = { 9 | images: { 10 | remotePatterns: [ 11 | { hostname: "lh3.googleusercontent.com" }, 12 | { hostname: 'youtu.be' } 13 | ], 14 | }, 15 | eslint: { 16 | ignoreDuringBuilds: true, 17 | } 18 | }; 19 | 20 | export default config; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tymely", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "next build", 8 | "db:push": "prisma db push", 9 | "db:studio": "prisma studio", 10 | "dev": "next dev --experimental-https", 11 | "postinstall": "prisma generate", 12 | "lint": "next lint", 13 | "start": "next start" 14 | }, 15 | "dependencies": { 16 | "@auth/prisma-adapter": "^1.4.0", 17 | "@google/generative-ai": "^0.11.3", 18 | "@hookform/resolvers": "^3.4.0", 19 | "@next-auth/prisma-adapter": "^1.0.7", 20 | "@prisma/client": "^5.10.2", 21 | "@radix-ui/react-avatar": "^1.0.4", 22 | "@radix-ui/react-checkbox": "^1.0.4", 23 | "@radix-ui/react-dialog": "^1.0.5", 24 | "@radix-ui/react-dropdown-menu": "^2.0.6", 25 | "@radix-ui/react-label": "^2.0.2", 26 | "@radix-ui/react-popover": "^1.0.7", 27 | "@radix-ui/react-progress": "^1.0.3", 28 | "@radix-ui/react-slot": "^1.0.2", 29 | "@radix-ui/react-tabs": "^1.0.4", 30 | "@radix-ui/react-tooltip": "^1.0.7", 31 | "@t3-oss/env-nextjs": "^0.10.1", 32 | "@tsparticles/engine": "^3.4.0", 33 | "@tsparticles/react": "^3.0.0", 34 | "@tsparticles/slim": "^3.4.0", 35 | "@types/uuid": "^9.0.8", 36 | "@vercel/analytics": "^1.3.1", 37 | "class-variance-authority": "^0.7.0", 38 | "clsx": "^2.1.1", 39 | "date-fns": "^3.6.0", 40 | "framer-motion": "^11.2.4", 41 | "geist": "^1.3.0", 42 | "lucide-react": "^0.378.0", 43 | "next": "^14.2.1", 44 | "next-auth": "^4.24.6", 45 | "next-themes": "^0.3.0", 46 | "react": "18.2.0", 47 | "react-day-picker": "^8.10.1", 48 | "react-dom": "18.2.0", 49 | "react-hook-form": "^7.51.4", 50 | "sonner": "^1.4.41", 51 | "tailwind-merge": "^2.3.0", 52 | "tailwindcss-animate": "^1.0.7", 53 | "uuid": "^9.0.1", 54 | "vaul": "^0.9.1", 55 | "zod": "^3.23.8" 56 | }, 57 | "devDependencies": { 58 | "@types/eslint": "^8.56.2", 59 | "@types/node": "^20.11.20", 60 | "@types/react": "^18.2.57", 61 | "@types/react-dom": "^18.2.19", 62 | "@typescript-eslint/eslint-plugin": "^7.1.1", 63 | "@typescript-eslint/parser": "^7.1.1", 64 | "eslint": "^8.57.0", 65 | "eslint-config-next": "^14.1.3", 66 | "postcss": "^8.4.34", 67 | "prettier": "^3.2.5", 68 | "prettier-plugin-tailwindcss": "^0.5.11", 69 | "prisma": "^5.10.2", 70 | "tailwindcss": "^3.4.1", 71 | "typescript": "^5.4.2" 72 | }, 73 | "ct3aMetadata": { 74 | "initVersion": "7.33.1" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | tailwindcss: {}, 4 | }, 5 | }; 6 | 7 | module.exports = config; 8 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ 2 | const config = { 3 | plugins: ["prettier-plugin-tailwindcss"], 4 | }; 5 | 6 | export default config; 7 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "postgresql" 10 | // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below 11 | // Further reading: 12 | // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema 13 | // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string 14 | url = env("DATABASE_URL") 15 | } 16 | 17 | // Necessary for Next auth 18 | model Account { 19 | id String @id @default(cuid()) 20 | userId String 21 | type String 22 | provider String 23 | providerAccountId String 24 | refresh_token String? // @db.Text 25 | access_token String? // @db.Text 26 | expires_at Int? 27 | token_type String? 28 | scope String? 29 | id_token String? // @db.Text 30 | session_state String? 31 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 32 | refresh_token_expires_in Int? 33 | 34 | @@unique([provider, providerAccountId]) 35 | } 36 | 37 | model Session { 38 | id String @id @default(cuid()) 39 | sessionToken String @unique 40 | userId String 41 | expires DateTime 42 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 43 | } 44 | 45 | model User { 46 | id String @id @default(cuid()) 47 | name String? 48 | email String? @unique 49 | emailVerified DateTime? 50 | image String? 51 | accounts Account[] 52 | sessions Session[] 53 | workFlows Workflows[] 54 | } 55 | 56 | model VerificationToken { 57 | identifier String 58 | token String @unique 59 | expires DateTime 60 | 61 | @@unique([identifier, token]) 62 | } 63 | 64 | model Workflows { 65 | id String @id @default(uuid()) 66 | vision String 67 | fromDate DateTime 68 | toDate DateTime 69 | duration Int 70 | mileStone mileStone[] 71 | User User @relation(fields: [userId], references: [id] , onDelete: Cascade) 72 | userId String 73 | } 74 | 75 | model mileStone { 76 | id String @id @default(uuid()) 77 | title String 78 | ToDate DateTime 79 | FromDate DateTime 80 | isCompleted Boolean @default(false) 81 | task Task[] 82 | workFlowId String 83 | Workflow Workflows @relation(fields: [workFlowId], references: [id], onDelete: Cascade) 84 | } 85 | 86 | enum TaskStatus { 87 | Pending 88 | Completed 89 | InProgress 90 | } 91 | 92 | model Task { 93 | id String @id @default(uuid()) 94 | title String 95 | isCompleted TaskStatus @default(Pending) 96 | mileStoneId String 97 | mileStone mileStone @relation(fields: [mileStoneId], references: [id], onDelete: Cascade) 98 | } -------------------------------------------------------------------------------- /public/TymelyManage.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyManage.jpeg -------------------------------------------------------------------------------- /public/TymelyManageLight.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyManageLight.jpeg -------------------------------------------------------------------------------- /public/TymelyPreview.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyPreview.jpeg -------------------------------------------------------------------------------- /public/TymelyPreviewLight.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyPreviewLight.jpeg -------------------------------------------------------------------------------- /public/TymelyTimeBlocker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyTimeBlocker.png -------------------------------------------------------------------------------- /public/TymelyTimeBlockerLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyTimeBlockerLight.png -------------------------------------------------------------------------------- /public/TymelyWorkflow.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyWorkflow.jpeg -------------------------------------------------------------------------------- /public/TymelyWorkflowLight.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/TymelyWorkflowLight.jpeg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/favicon.ico -------------------------------------------------------------------------------- /public/tymely-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Omm2005/Tymely/6a05ce2443f989bf2e590a35a396ba2e1ebc3fce/public/tymely-logo.png -------------------------------------------------------------------------------- /src/Provider/modal-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React , { createContext, useContext, useEffect, useState } from "react" 4 | 5 | interface ModalProviderProps { 6 | children: React.ReactNode 7 | } 8 | 9 | export type ModalData = {} 10 | 11 | type ModalContextType = { 12 | data: ModalData, 13 | isOpen: boolean, 14 | setOpen: (modal: React.ReactNode , fetchData?: () => Promise) => void, 15 | setClose: () => void 16 | setLoading: (bool: boolean) => void 17 | isLoading: boolean 18 | } 19 | 20 | export const ModalContext = createContext({ 21 | data: {}, 22 | isOpen: false, 23 | setOpen: (modal: React.ReactNode , fetchData?: () => Promise) => {}, 24 | setClose: () => {}, 25 | setLoading: (bool: boolean) => {}, 26 | isLoading: false 27 | }) 28 | 29 | const ModalProvider: React.FC = ({children}) => { 30 | const [isOpen , setIsOpen] = useState(false) 31 | const [data, setData] = useState({}) 32 | const [showingModal, setShowingModal] = useState(null) 33 | const [isMounted, setIsMounted] = useState(false) 34 | const [isLoading, setIsLoading] = useState(false) 35 | 36 | useEffect(() => { 37 | setIsMounted(true) 38 | }, []) 39 | 40 | const setOpen = async (modal: React.ReactNode , fetchData?: () => Promise) => { 41 | if(modal) { 42 | if(fetchData){ 43 | setData({...data, ...(await fetchData())} || {}) 44 | } 45 | setShowingModal(modal) 46 | setIsOpen(true) 47 | } 48 | } 49 | 50 | const setLoading = (bool: boolean) => { 51 | setIsLoading(bool) 52 | } 53 | 54 | const setClose = () => { 55 | if(!isLoading) { 56 | setIsOpen(false) 57 | setData({}) 58 | } 59 | } 60 | 61 | if(!isMounted) { 62 | return null 63 | } 64 | 65 | return ( 66 | 67 | {children} 68 | {showingModal} 69 | 70 | ) 71 | } 72 | 73 | export const useModal = () => { 74 | const context = useContext(ModalContext) 75 | if(context === undefined) { 76 | throw new Error('useModal must be used within a ModalProvider') 77 | } 78 | return context 79 | } 80 | 81 | export default ModalProvider -------------------------------------------------------------------------------- /src/Provider/theme-provider.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 ( 9 | 10 | {children} 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/Provider/toast-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useTheme } from 'next-themes' 4 | import React from 'react' 5 | import { Toaster } from 'sonner' 6 | 7 | const ToastProvider = () => { 8 | const { theme , systemTheme } = useTheme() 9 | const isDark = theme === 'dark' || (theme === 'system' && systemTheme === 'dark') 10 | return ( 11 | 12 | ) 13 | } 14 | 15 | export default ToastProvider -------------------------------------------------------------------------------- /src/app/(pages)/workflows/[id]/board/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SparklesCore } from '@/components/global/sparkles'; 3 | 4 | type Props = {} 5 | 6 | const page = (props: Props) => { 7 | return ( 8 |
9 |
10 |

11 | Coming Soon... 12 |

13 |
14 | {/* Gradients */} 15 |
16 |
17 |
18 |
19 | 20 | {/* Core component */} 21 | 29 | 30 | {/* Radial Gradient to prevent sharp edges */} 31 |
32 |
33 |
34 |
35 | ) 36 | } 37 | 38 | export default page -------------------------------------------------------------------------------- /src/app/(pages)/workflows/[id]/calendar/page.tsx: -------------------------------------------------------------------------------- 1 | import CalendarComponent from '@/components/workflow/Calendar' 2 | import { getMilestones } from '@/server/actions' 3 | import { redirect } from 'next/navigation' 4 | import React from 'react' 5 | 6 | const page = async ({ 7 | params: { 8 | id, 9 | } 10 | }: { 11 | params: { 12 | id: string 13 | } 14 | }) => { 15 | const milestones = await getMilestones(id) 16 | if(!milestones) { 17 | return redirect('/workflows') 18 | } 19 | 20 | return ( 21 |
22 | 23 |
24 | ); 25 | } 26 | 27 | export default page -------------------------------------------------------------------------------- /src/app/(pages)/workflows/[id]/flow/page.tsx: -------------------------------------------------------------------------------- 1 | import MilestoneEdit from '@/components/workflow/milestoneEdit' 2 | import { getWorkflow } from '@/server/actions' 3 | import { redirect } from 'next/navigation' 4 | import React from 'react' 5 | 6 | type Props = { 7 | params: { 8 | id: string 9 | } 10 | } 11 | 12 | const page = async ({ 13 | params: { id } 14 | }: Props) => { 15 | const workflow = await getWorkflow(id) 16 | if(!workflow) return redirect('/workflows') 17 | return ( 18 |
19 | 20 |
21 | ) 22 | } 23 | 24 | export default page -------------------------------------------------------------------------------- /src/app/(pages)/workflows/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | import WorkflowNavbar from '@/components/workflow/WorkflowNavbar' 2 | import { ModeToggle } from '@/components/global/ModeToggle' 3 | import UserAvatar from '@/components/global/useravatar' 4 | import { getServerAuthSession } from '@/server/auth' 5 | import Link from 'next/link' 6 | import { redirect } from 'next/navigation' 7 | import React from 'react' 8 | import Image from 'next/image' 9 | import TymelyLogo from '@/../public/tymely-logo.png' 10 | import { Metadata } from 'next' 11 | import TymelyLogoIco from '@/../public/favicon.ico' 12 | 13 | const title = "Workflow | Tymely"; 14 | const description = "This is the workflow page of Tymely. Here you can create, edit, delete and manage workflows."; 15 | const image = 'https://tymely.vercel.app/tymely-logo.png' 16 | 17 | export const metadata: Metadata = { 18 | title, 19 | description, 20 | icons: [TymelyLogoIco.src], 21 | openGraph: { 22 | title, 23 | description, 24 | images: [image], 25 | }, 26 | twitter: { 27 | card: "summary_large_image", 28 | title, 29 | description, 30 | images: [image], 31 | creator: "@maiommhoon", 32 | }, 33 | metadataBase: new URL("https://tymely.vercel.app/"), 34 | }; 35 | 36 | const FlowLayout = async ({ 37 | children 38 | }: { 39 | children: React.ReactNode 40 | }) => { 41 | const session = await getServerAuthSession() 42 | if (!session || !session.user) { 43 | return redirect('/') 44 | } 45 | const user = session.user 46 | return ( 47 |
48 |
49 |
50 | 51 | A Cute Tymely Logo 52 | 53 | 54 | 55 |
56 |
57 | 58 |
59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 |
67 |
68 |
69 | {children} 70 |
71 | ) 72 | } 73 | 74 | export default FlowLayout -------------------------------------------------------------------------------- /src/app/(pages)/workflows/page.tsx: -------------------------------------------------------------------------------- 1 | import { getServerAuthSession } from '@/server/auth' 2 | import React from 'react' 3 | import UserAvatar from '@/components/global/useravatar' 4 | import { ModeToggle } from '@/components/global/ModeToggle' 5 | import WorkflowButton from '@/components/workflowButton' 6 | import { redirect } from 'next/navigation' 7 | import { onGetWorkflows } from '@/server/actions' 8 | import WorkflowCard from '@/components/workflowCard' 9 | 10 | const WorkflowsPage = async() => { 11 | const session = await getServerAuthSession() 12 | const user = session?.user 13 | if(!user) { 14 | return redirect('/') 15 | } 16 | const workflows = await onGetWorkflows() 17 | return ( 18 |
19 |
20 |
21 |

22 | Workflows 23 |

24 |
25 |
26 | 27 |
28 | 29 | 30 |
31 |
32 |
33 | { 34 | workflows?.map((workflow) => { 35 | return ( 36 | 37 | ) 38 | } 39 | )} 40 |
41 |
42 | 43 |
44 |
45 |
46 | ) 47 | } 48 | 49 | export default WorkflowsPage -------------------------------------------------------------------------------- /src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | 3 | import { authOptions } from "@/server/auth"; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 6 | const handler = NextAuth(authOptions); 7 | export { handler as GET, handler as POST }; -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css"; 2 | 3 | import type { Metadata } from "next"; 4 | import { DM_Sans } from "next/font/google"; 5 | import { ThemeProvider } from "@/Provider/theme-provider"; 6 | import ModalProvider from "@/Provider/modal-provider"; 7 | import ToastProvider from "@/Provider/toast-provider"; 8 | import { Analytics } from "@vercel/analytics/react" 9 | import TymelyLogoIco from '@/../public/favicon.ico' 10 | 11 | const font = DM_Sans({ subsets: ["latin"] }); 12 | const title = "Tymely"; 13 | const description = "It's Time to not waste it.An AI poweered time management tool. Tymely helps you to manage your time effectively which can convert your vision into actionable tasks."; 14 | const image = 'https://tymely.vercel.app/tymely-logo.png' 15 | 16 | export const metadata: Metadata = { 17 | title, 18 | description, 19 | icons: [TymelyLogoIco.src], 20 | openGraph: { 21 | title, 22 | description, 23 | images: [image], 24 | }, 25 | twitter: { 26 | card: "summary_large_image", 27 | title, 28 | description, 29 | images: [image], 30 | creator: "@maiommhoon", 31 | }, 32 | metadataBase: new URL("https://tymely.vercel.app/"), 33 | }; 34 | 35 | 36 | export default function RootLayout({ 37 | children, 38 | }: Readonly<{ 39 | children: React.ReactNode; 40 | }>) { 41 | return ( 42 | 43 | 44 | 50 | 51 | 52 | {children} 53 | 54 | 55 | 56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import LoginButton from "@/components/global/LoginButton"; 2 | import Navbar from "@/components/global/Navbar"; 3 | import AnimatedGradientText from "@/components/ui/animated-gradient-text"; 4 | import ShimmerButton from "@/components/ui/shimmer-button"; 5 | import { cn } from "@/lib/utils"; 6 | import { getServerAuthSession } from "@/server/auth"; 7 | import { ChevronRight } from "lucide-react"; 8 | import Image from "next/image"; 9 | import Link from "next/link"; 10 | import { BorderBeam } from "@/components/ui/shimmer-border-beam"; 11 | import BoxReveal from "@/components/ui/box-reveal"; 12 | import NumberTicker from "@/components/ui/number-ticker"; 13 | import { BentoGridSecondDemo } from "@/components/Bento"; 14 | import TymelyLogo from "@/../public/tymely-logo.png" 15 | import { Button } from "@/components/ui/button"; 16 | import WordPullUp from "@/components/ui/words-pullup"; 17 | import RadialGradient from "@/components/global/radical-gradient"; 18 | import ThemeImage from "@/components/global/theme-image-changer"; 19 | import TymelyPreview from "@/../public/TymelyPreview.jpeg" 20 | import TymelyPreviewLight from '@/../public/TymelyPreviewLight.jpeg' 21 | 22 | export default async function Home() { 23 | const session = await getServerAuthSession() 24 | const user = session?.user 25 | return ( 26 |
27 | 28 |
29 | 30 | 31 | 32 | Star on Github 33 | 34 | 35 |

36 | Spend time to things that 37 | . 41 | Create your plan with us and just . 45 |

46 | { 47 | user ? 48 | ( 49 | 53 | 54 | 59 | 🎉
{" "} Just Start Building 60 |
61 | 62 |
63 | 64 | ) : 65 | } 66 | 67 | {/* */} 68 | {/* Button */} 69 |
70 |
71 |
72 |

73 | approx. 74 |

75 | minutes 76 |
77 |

78 | are wasted per day on planning their life. 79 |

80 |
81 |
82 |
83 |

84 | approx. 85 |

86 | % 87 |
88 |

89 | People quit during planning their goal. 90 |

91 |
92 | 93 | {/*
*/} 94 |

95 | We help you to create your plan, 96 |
97 | so you can focus on building your idea. 98 |

99 | {/*
*/} 100 |
101 |
102 |
103 | {/* 104 | Tymely Preview */} 110 | 111 | 112 |
113 |
114 | 115 | 116 |
117 | {/* */} 118 |
119 |

120 | Touch 121 | 122 | Grass 123 | 124 |
125 | and Start 126 | 127 | Building Stuff 128 | 129 |

130 | { 131 | user ? 132 | ( 133 | 137 | 138 | 143 | 🎉
{" "} Just Start Building 144 |
145 | 146 |
147 | 148 | ) : 149 | } 150 | 151 |
152 | 153 | 154 |
155 | 156 |
157 | 158 |
159 |
160 | 167 | 181 |
182 |
183 | ); 184 | } -------------------------------------------------------------------------------- /src/components/Bento.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from "react"; 4 | import { BentoGrid, BentoGridItem } from "@/components/ui/bento-grid"; 5 | import { 6 | CirclePlus, 7 | KanbanSquare, 8 | LayoutTemplate, 9 | SquareGanttChart, 10 | } from "lucide-react"; 11 | import Image from "next/image"; 12 | import TymelyPreview from "@/../public/TymelyPreview.jpeg"; 13 | import TymelyPreviewLight from "@/../public/TymelyPreviewLight.jpeg"; 14 | import TymelyWorkflow from "@/../public/TymelyWorkflow.jpeg"; 15 | import TymelyWorkflowLight from "@/../public/TymelyWorkflowLight.jpeg"; 16 | import TymelyManage from '@/../public/TymelyManage.jpeg' 17 | import TymelyManageLight from '@/../public/TymelyManageLight.jpeg' 18 | import TymelyTimeBlocker from '@/../public/TymelyTimeBlocker.png' 19 | import TymelyTimeBlockerLight from '@/../public/TymelyTimeBlockerLight.png' 20 | import { useTheme } from "next-themes"; 21 | 22 | export function BentoGridSecondDemo() { 23 | const {theme} = useTheme(); 24 | const items = [ 25 | { 26 | title: "Your AI planner", 27 | description: "Plan your ideas with AI within seconds.", 28 | header: TymelyPreview, 29 | className: "md:col-span-2", 30 | icon: , 31 | commingSoon: false, 32 | }, 33 | { 34 | title: "Easy to edit user interface", 35 | description: "Easily edit your project and plan.", 36 | header: TymelyPreview, 37 | className: "md:col-span-1", 38 | icon: , 39 | commingSoon: false, 40 | }, 41 | { 42 | title: "Manage your tasks", 43 | description: "Manage your tasks and work on them with ease.", 44 | header: TymelyPreview, 45 | className: "md:col-span-1", 46 | icon: , 47 | commingSoon: false, 48 | }, 49 | { 50 | title: "Organize your life.", 51 | description: 52 | "Organize your life and plan your weeks.", 53 | header: TymelyPreview, 54 | className: "md:col-span-2", 55 | icon: , 56 | commingSoon: true, 57 | }, 58 | ]; 59 | return ( 60 | 61 | {items.map((item, i) => ( 62 | 71 | ))} 72 | 73 | ); 74 | } -------------------------------------------------------------------------------- /src/components/forms/flow-form.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { EditMilestoneType, Milestone, VisionFormSchema } from '@/lib/types' 4 | import { v4 as uuidv4 } from 'uuid'; 5 | import { useModal } from '@/Provider/modal-provider' 6 | import { zodResolver } from '@hookform/resolvers/zod' 7 | import React from 'react' 8 | import { useFieldArray, useForm } from 'react-hook-form' 9 | import { z } from 'zod' 10 | import { Card, CardContent } from '../ui/card' 11 | import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '../ui/form' 12 | import { toast } from 'sonner' 13 | import { Textarea } from '../ui/textarea' 14 | import { Button } from '../ui/button' 15 | import { CalendarIcon, Loader2, Trash } from 'lucide-react' 16 | import { addNewMilestone, onEditMilestone, onVisionChange } from '@/server/actions' 17 | import { useRouter } from 'next/navigation' 18 | import { Input } from '../ui/input' 19 | import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; 20 | import { format } from 'date-fns' 21 | import { Calendar } from '../ui/calendar' 22 | import { useClientMediaQuery } from '@/hooks/useClientMediaQuery' 23 | import { cn } from '@/lib/utils' 24 | 25 | type Props = { 26 | workflowId: string, 27 | type: 'vision' | 'milestone' | 'newMilestone', 28 | visionTitle?: string, 29 | milestone?: { 30 | id: string; 31 | task: { 32 | id: string; 33 | title: string; 34 | }[]; 35 | title: string; 36 | ToDate: Date; 37 | FromDate: Date; 38 | }, 39 | duration: { 40 | from: Date, 41 | to: Date 42 | } 43 | } 44 | 45 | 46 | const EditVision = ({ vision, workflowId }: { vision: string, workflowId: string }) => { 47 | const { setClose, setLoading } = useModal() 48 | const router = useRouter() 49 | const form = useForm>({ 50 | mode: 'onChange', 51 | resolver: zodResolver(VisionFormSchema), 52 | defaultValues: { 53 | vision: vision, 54 | }, 55 | }) 56 | 57 | const OnSubmit = async (data: z.infer) => { 58 | setLoading(true) 59 | try { 60 | const result = await onVisionChange(data.vision, workflowId) 61 | if (result) { 62 | setClose() 63 | toast.success('Vision updated') 64 | router.refresh() 65 | } 66 | } catch (error) { 67 | toast.error('Error updating vision') 68 | } 69 | setLoading(false) 70 | } 71 | 72 | const isLoading = form.formState.isSubmitting 73 | 74 | return ( 75 | 76 | 77 | 78 | 79 |
80 | 84 | ( 89 | 90 | Vision 91 | 92 |