├── backend ├── public │ ├── favicon.ico │ ├── robots.txt │ ├── index.php │ └── .htaccess ├── resources │ ├── css │ │ └── app.css │ └── js │ │ ├── app.js │ │ ├── bootstrap.js │ │ └── echo.js ├── database │ ├── .gitignore │ ├── seeders │ │ └── DatabaseSeeder.php │ ├── migrations │ │ ├── 2024_03_20_163112_create_comments_table.php │ │ ├── 0001_01_01_000001_create_cache_table.php │ │ ├── 2024_03_15_123846_create_personal_access_tokens_table.php │ │ ├── 2024_03_17_105321_create_posts_table.php │ │ ├── 0001_01_01_000000_create_users_table.php │ │ └── 0001_01_01_000002_create_jobs_table.php │ └── factories │ │ └── UserFactory.php ├── storage │ ├── logs │ │ └── .gitignore │ ├── app │ │ ├── public │ │ │ └── .gitignore │ │ └── .gitignore │ └── framework │ │ ├── sessions │ │ └── .gitignore │ │ ├── testing │ │ └── .gitignore │ │ ├── views │ │ └── .gitignore │ │ ├── cache │ │ ├── data │ │ │ └── .gitignore │ │ └── .gitignore │ │ └── .gitignore ├── bootstrap │ ├── cache │ │ └── .gitignore │ ├── providers.php │ └── app.php ├── tests │ ├── Unit │ │ └── ExampleTest.php │ ├── Feature │ │ └── ExampleTest.php │ ├── TestCase.php │ └── Pest.php ├── app │ ├── Http │ │ ├── Controllers │ │ │ ├── Controller.php │ │ │ └── API │ │ │ │ ├── UserController.php │ │ │ │ ├── PostController.php │ │ │ │ ├── CommentController.php │ │ │ │ └── AuthController.php │ │ └── Requests │ │ │ └── PostRequest.php │ ├── Providers │ │ └── AppServiceProvider.php │ ├── Models │ │ ├── Comment.php │ │ ├── Post.php │ │ └── User.php │ └── Events │ │ ├── TestEvent.php │ │ ├── CommentIncrement.php │ │ └── PostBroadCastEvent.php ├── routes │ ├── web.php │ ├── channels.php │ ├── console.php │ └── api.php ├── .gitattributes ├── package.json ├── vite.config.js ├── .gitignore ├── .editorconfig ├── artisan ├── config │ ├── cors.php │ ├── services.php │ ├── filesystems.php │ ├── reverb.php │ ├── broadcasting.php │ ├── sanctum.php │ ├── mail.php │ ├── cache.php │ ├── queue.php │ ├── auth.php │ ├── app.php │ ├── logging.php │ ├── database.php │ └── session.php ├── phpunit.xml ├── .env.example ├── composer.json └── README.md ├── .DS_Store ├── frontend ├── public │ ├── avatar.png │ ├── favicon.ico │ └── logo.svg ├── postcss.config.js ├── src │ ├── middleware.ts │ ├── lib │ │ ├── env.ts │ │ ├── axios.config.ts │ │ ├── apiEndPoints.ts │ │ ├── utils.ts │ │ └── echo.config.ts │ ├── app │ │ ├── api │ │ │ ├── auth │ │ │ │ └── [...nextauth] │ │ │ │ │ ├── route.ts │ │ │ │ │ └── authOptions.ts │ │ │ └── image-preview │ │ │ │ └── route.ts │ │ ├── not-found.tsx │ │ ├── (daily_dev) │ │ │ ├── page.tsx │ │ │ └── layout.tsx │ │ ├── layout.tsx │ │ ├── login │ │ │ └── page.tsx │ │ └── globals.css │ ├── components │ │ ├── base │ │ │ ├── Sidebar.tsx │ │ │ ├── SearchInput.tsx │ │ │ ├── MobileSidebar.tsx │ │ │ ├── Navbar.tsx │ │ │ ├── SidebarLinks.tsx │ │ │ └── ProfileMenu.tsx │ │ ├── common │ │ │ └── UserAvatar.tsx │ │ ├── comment │ │ │ ├── CommentCard.tsx │ │ │ └── AddComment.tsx │ │ ├── ui │ │ │ ├── label.tsx │ │ │ ├── textarea.tsx │ │ │ ├── input.tsx │ │ │ ├── button.tsx │ │ │ ├── tabs.tsx │ │ │ ├── card.tsx │ │ │ ├── dialog.tsx │ │ │ ├── sheet.tsx │ │ │ └── dropdown-menu.tsx │ │ ├── post │ │ │ ├── ShowPost.tsx │ │ │ ├── PostCard.tsx │ │ │ ├── Posts.tsx │ │ │ └── AddPost.tsx │ │ └── auth │ │ │ ├── Login.tsx │ │ │ └── Register.tsx │ ├── providers │ │ └── AuthProvider.tsx │ └── dataFetch │ │ └── postFetch.ts ├── .env.example ├── next.config.mjs ├── components.json ├── .gitignore ├── tsconfig.json ├── package.json ├── types.ts ├── README.md └── tailwind.config.ts └── README.md /backend/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/resources/css/app.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /backend/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | -------------------------------------------------------------------------------- /backend/storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /backend/storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /backend/storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TusharVashishth/daily-dev-clone/HEAD/.DS_Store -------------------------------------------------------------------------------- /backend/bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /frontend/src/middleware.ts: -------------------------------------------------------------------------------- 1 | export { default } from "next-auth/middleware"; 2 | 3 | export const config = { matcher: ["/", "/profile"] }; 4 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /backend/storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /frontend/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | export default class Env { 2 | static API_URL: string = process.env.NEXT_PUBLIC_API_URL as string; 3 | static APP_URL: string = process.env.NEXT_PUBLIC_NEXTAUTH_URL as string; 4 | } 5 | -------------------------------------------------------------------------------- /backend/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 9 | // return true; 10 | }); 11 | -------------------------------------------------------------------------------- /backend/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /backend/routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 8 | })->purpose('Display an inspiring quote')->hourly(); 9 | -------------------------------------------------------------------------------- /frontend/src/lib/axios.config.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { API_URL } from "./apiEndPoints"; 3 | 4 | const myAxios = axios.create({ 5 | baseURL: API_URL, 6 | headers: { 7 | Accept: "application/json", 8 | }, 9 | }); 10 | 11 | export default myAxios; 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Daily Dev Clone 2 | 3 | Let's Build Daily Dev Clone using the future by using future-proof technology Laravel 11 as the backend and Next js 14 with typescript as the Front end. 4 | 5 | In this video, we will also explore Reverb for real-time updates. 6 | 7 | If you use this code then forget to give this star ⭐️⭐️ 8 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "axios": "^1.6.4", 10 | "laravel-vite-plugin": "^1.0", 11 | "vite": "^5.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: ['resources/css/app.css', 'resources/js/app.js'], 8 | refresh: true, 9 | }), 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /frontend/src/components/base/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | import SidebarLinks from "./SidebarLinks"; 6 | 7 | export default function Sidebar() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/providers/AuthProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { SessionProvider } from "next-auth/react"; 5 | 6 | export default function AuthProvider({ 7 | children, 8 | }: { 9 | children: React.ReactNode; 10 | }) { 11 | return {children}; 12 | } 13 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpunit.result.cache 12 | Homestead.json 13 | Homestead.yaml 14 | auth.json 15 | npm-debug.log 16 | yarn-error.log 17 | /.fleet 18 | /.idea 19 | /.vscode 20 | -------------------------------------------------------------------------------- /backend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_NEXTAUTH_URL=http://localhost:3000 2 | NEXTAUTH_SECRET="YOUR_KEY" 3 | NEXT_PUBLIC_API_URL=http://localhost:8000 4 | 5 | NEXT_PUBLIC_REVERB_APP_ID=266721 6 | NEXT_PUBLIC_REVERB_APP_KEY=YOUR_KEY 7 | NEXT_PUBLIC_REVERB_APP_SECRET=YOUR_KEY 8 | NEXT_PUBLIC_REVERB_HOST="localhost" 9 | NEXT_PUBLIC_REVERB_PORT=8080 10 | NEXT_PUBLIC_REVERB_SCHEME=http -------------------------------------------------------------------------------- /frontend/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | hostname: "*", 7 | protocol: "https", 8 | }, 9 | { 10 | hostname: "localhost", 11 | protocol: "http", 12 | }, 13 | ], 14 | }, 15 | reactStrictMode: false, 16 | }; 17 | 18 | export default nextConfig; 19 | -------------------------------------------------------------------------------- /backend/artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /backend/resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | window.axios = axios; 3 | 4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 5 | 6 | /** 7 | * Echo exposes an expressive API for subscribing to channels and listening 8 | * for events that are broadcast by Laravel. Echo and event broadcasting 9 | * allow your team to quickly build robust real-time web applications. 10 | */ 11 | 12 | import './echo'; 13 | -------------------------------------------------------------------------------- /frontend/src/lib/apiEndPoints.ts: -------------------------------------------------------------------------------- 1 | import Env from "./env"; 2 | 3 | export const API_URL = Env.API_URL + "/api"; 4 | export const LOGIN_URL = "/auth/login"; 5 | export const REGISTER_URL = "/auth/register"; 6 | export const CHECK_CREDENTIALS = "/auth/checkCredentials"; 7 | export const LOGOUT_URL = "/auth/logout"; 8 | 9 | export const POST_URL = "/post"; 10 | export const COMMENT_URL = "/comment"; 11 | export const UPDATE_PROFILE = "/update/profile"; 12 | -------------------------------------------------------------------------------- /frontend/src/components/common/UserAvatar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Image from "next/image"; 3 | import { getImageUrl } from "@/lib/utils"; 4 | export default function UserAvatar({ image }: { image?: string }) { 5 | return ( 6 |
7 | {image ? ( 8 | user 9 | ) : ( 10 | logo 11 | )} 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /backend/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /backend/resources/js/echo.js: -------------------------------------------------------------------------------- 1 | import Echo from 'laravel-echo'; 2 | 3 | import Pusher from 'pusher-js'; 4 | window.Pusher = Pusher; 5 | 6 | window.Echo = new Echo({ 7 | broadcaster: 'reverb', 8 | key: import.meta.env.VITE_REVERB_APP_KEY, 9 | wsHost: import.meta.env.VITE_REVERB_HOST, 10 | wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, 11 | wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, 12 | forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', 13 | enabledTransports: ['ws', 'wss'], 14 | }); 15 | -------------------------------------------------------------------------------- /backend/public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /backend/app/Models/Comment.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class, "user_id", "id")->select("id", "profile_image", "username", "name"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/.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 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /frontend/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import Image from "next/image"; 3 | import Link from "next/link"; 4 | 5 | export default function NotFound() { 6 | return ( 7 |
8 | 404 15 | 16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/dataFetch/postFetch.ts: -------------------------------------------------------------------------------- 1 | import { API_URL, POST_URL } from "@/lib/apiEndPoints"; 2 | 3 | export async function getPosts(token: string, nextPage?: string) { 4 | const res = await fetch(API_URL + POST_URL, { 5 | headers: { 6 | Authorization: `Bearer ${token}`, 7 | }, 8 | }); 9 | // The return value is *not* serialized 10 | // You can return Date, Map, Set, etc. 11 | 12 | if (!res.ok) { 13 | // This will activate the closest `error.js` Error Boundary 14 | throw new Error("Failed to fetch data"); 15 | } 16 | 17 | return res.json(); 18 | } 19 | -------------------------------------------------------------------------------- /backend/app/Models/Post.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class, "user_id", "id")->select("id", "profile_image"); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /backend/database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | User::factory()->create([ 19 | 'name' => 'Test User', 20 | 'email' => 'test@example.com', 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/app/(daily_dev)/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getServerSession } from "next-auth"; 3 | import { 4 | CustomSession, 5 | authOptions, 6 | } from "../api/auth/[...nextauth]/authOptions"; 7 | import { getPosts } from "@/dataFetch/postFetch"; 8 | import Posts from "@/components/post/Posts"; 9 | 10 | export default async function App() { 11 | const session = (await getServerSession(authOptions)) as CustomSession; 12 | const posts: APIResponseType = await getPosts( 13 | session.user?.token! 14 | ); 15 | return ( 16 |
17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/api/image-preview/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import { getLinkPreview } from "link-preview-js"; 3 | import { unstable_noStore } from "next/cache"; 4 | 5 | export async function POST(request: NextRequest) { 6 | unstable_noStore(); 7 | try { 8 | const body = await request.json(); 9 | const data: ImagePreviewResType = (await getLinkPreview(body.url, { 10 | imagesPropertyType: "og", 11 | followRedirects: "follow", 12 | })) as ImagePreviewResType; 13 | return NextResponse.json({ status: 200, data }); 14 | } catch (error) { 15 | return NextResponse.json({ status: 404, message: "Not found" }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /backend/bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | api: __DIR__.'/../routes/api.php', 11 | commands: __DIR__.'/../routes/console.php', 12 | channels: __DIR__.'/../routes/channels.php', 13 | health: '/up', 14 | ) 15 | ->withMiddleware(function (Middleware $middleware) { 16 | // 17 | }) 18 | ->withExceptions(function (Exceptions $exceptions) { 19 | // 20 | })->create(); 21 | -------------------------------------------------------------------------------- /frontend/src/components/base/MobileSidebar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import { 4 | Sheet, 5 | SheetContent, 6 | SheetDescription, 7 | SheetHeader, 8 | SheetTitle, 9 | SheetTrigger, 10 | } from "@/components/ui/sheet"; 11 | import { Menu } from "lucide-react"; 12 | import SidebarLinks from "./SidebarLinks"; 13 | 14 | export default function MobileSidebar() { 15 | const [open, setOpen] = useState(false); 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/components/comment/CommentCard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import UserAvatar from "../common/UserAvatar"; 3 | import { formatDate } from "@/lib/utils"; 4 | 5 | export default function CommentCard({ comment }: { comment: CommentType }) { 6 | return ( 7 |
8 |
9 | 10 |
11 |

{comment.user.name}

12 |

13 | @{comment.user.username} . {formatDate(comment.created_at)} 14 |

15 |
16 |
17 |

{comment.comment}

18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | import moment from "moment"; 4 | import Env from "./env"; 5 | 6 | export function cn(...inputs: ClassValue[]) { 7 | return twMerge(clsx(inputs)); 8 | } 9 | 10 | export const isValidUrl = (url: string): boolean => { 11 | try { 12 | new URL(url); 13 | return true; 14 | } catch (error) { 15 | return false; 16 | } 17 | }; 18 | 19 | export const trimString = (data: string) => { 20 | if (data.length <= 50) { 21 | return data; 22 | } 23 | return data.slice(0, 50) + "..."; 24 | }; 25 | 26 | export const formatDate = (date: string) => { 27 | return moment(date).format("DD MMM YYYY"); 28 | }; 29 | 30 | export const getImageUrl = (path: string): string => { 31 | return `${Env.API_URL}/storage/${path}`; 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/src/app/(daily_dev)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/base/Navbar"; 2 | import Sidebar from "@/components/base/Sidebar"; 3 | import { getServerSession } from "next-auth"; 4 | import { 5 | CustomSession, 6 | authOptions, 7 | } from "../api/auth/[...nextauth]/authOptions"; 8 | 9 | export default async function DailyDevLayout({ 10 | children, 11 | }: Readonly<{ 12 | children: React.ReactNode; 13 | }>) { 14 | const session = (await getServerSession(authOptions)) as CustomSession; 15 | return ( 16 |
17 | 18 |
19 | 20 |
21 | {children} 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/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 | -------------------------------------------------------------------------------- /frontend/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 |