diff --git a/.env.example b/.env.example index 5bc2afbce..7f47ba24d 100644 --- a/.env.example +++ b/.env.example @@ -21,6 +21,8 @@ NEXT_PUBLIC_DISABLE_FEATURES = "featurea,featureb,featurec" REDIS_URL= GITHUB_ID= GITHUB_SECRET= +NEXT_PUBLIC_DISCORD_WEBHOOK_URL = +JOB_BOARD_AUTH_SECRET= COHORT3_DISCORD_ACCESS_KEY = diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 000000000..220d6d0da --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,43 @@ +name: Continuous Deployment +on: + push: + branches: [ main ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Docker login + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + file: ./Dockerfile.prod + push: true + tags: 100xdevs/cms-staging:${{ github.sha }} + build-args: | + DATABASE_URL=${{ secrets.STAGING_DATABASE }} + + - name: Clone staging-ops repo, update, and push + env: + PAT: ${{ secrets.PAT }} + run: | + git clone https://github.com/code100x/staging-ops.git + cd staging-ops + sed -i 's|image: 100xdevs/cms-staging:.*|image: 100xdevs/cms-staging:${{ github.sha }}|' staging/cms/deployment.yml + git config user.name "GitHub Actions Bot" + git config user.email "actions@github.com" + git add staging/cms/deployment.yml + git commit -m "Update cms image to ${{ github.sha }}" + git push https://${PAT}@github.com/code100x/staging-ops.git main diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml new file mode 100644 index 000000000..7d2b2c100 --- /dev/null +++ b/.github/workflows/cd_prod.yml @@ -0,0 +1,43 @@ +name: Continuous Deployment (Prod) +on: + push: + branches: [ production ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Docker login + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + file: ./Dockerfile.prod + push: true + tags: 100xdevs/cms:${{ github.sha }} + build-args: | + DATABASE_URL=${{ secrets.PROD_DATABASE }} + + - name: Clone staging-ops repo, update, and push + env: + PAT: ${{ secrets.PAT }} + run: | + git clone https://github.com/code100x/staging-ops.git + cd staging-ops + sed -i 's|image: 100xdevs/cms:.*|image: 100xdevs/cms:${{ github.sha }}|' prod/cms/deployment.yml + git config user.name "GitHub Actions Bot" + git config user.email "actions@github.com" + git add prod/cms/deployment.yml + git commit -m "Update cms image to ${{ github.sha }}" + git push https://${PAT}@github.com/code100x/staging-ops.git main diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a08092d1a..0c412bee1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -3,7 +3,7 @@ on: pull_request: branches: - '**' - + jobs: Continuous-Integration: @@ -15,10 +15,10 @@ jobs: uses: actions/checkout@v3 - name: Install Dependencies - run: npm install --legacy-peer-deps + run: pnpm install --legacy-peer-deps - name: Run linting check - run: npm run lint:check + run: pnpm run lint:check - name: Check formatting - run: npm run format:check \ No newline at end of file + run: pnpm run format:check diff --git a/README.md b/README.md index 3cfaa43cc..d6b9692c7 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,7 @@ git clone https://github.com/code100x/cms.git ``` -```bash - -git clone https://github.com/code100x/cms.git -``` 2. Navigate to the project directory: @@ -42,11 +38,7 @@ cd cms ``` -```bash - -cd cms -``` 3. Run the following command to start the application: @@ -58,13 +50,7 @@ docker-compose up ``` -```bash - -docker volume create postgres-data # (optional) run this command if you face any mount volume / volume not exist error -docker-compose up - -``` ### Without Docker diff --git a/package.json b/package.json index b1454d812..f26aa8e56 100644 --- a/package.json +++ b/package.json @@ -47,10 +47,11 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-scroll-area": "^1.1.0", + "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", - "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-tooltip": "^1.0.7", "@tabler/icons-react": "^3.14.0", "@types/bcrypt": "^5.0.2", diff --git a/src/actions/videopreview/videoPreview.tsx b/src/actions/videopreview/videoPreview.tsx index 3e0611227..5c1405c95 100644 --- a/src/actions/videopreview/videoPreview.tsx +++ b/src/actions/videopreview/videoPreview.tsx @@ -1,18 +1,19 @@ 'use server'; -import db from '@/db'; +// import db from '@/db'; export default async function VideoPreview({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars contentId, }: { contentId: number; }) { - const videoMetadata = await db.videoMetadata.findFirst({ - where: { contentId }, - select: { video_360p_1: true }, - }); + // const videoMetadata = await db.videoMetadata.findFirst({ + // where: { contentId }, + // select: { video_360p_1: true }, + // }); - if (videoMetadata) { - return videoMetadata.video_360p_1; - } + // if (videoMetadata) { + // return videoMetadata.video_360p_1; + // } return null; } diff --git a/src/app/(main)/(pages)/home/page.tsx b/src/app/(main)/(pages)/home/page.tsx index 281c8b9ea..d095f57b9 100644 --- a/src/app/(main)/(pages)/home/page.tsx +++ b/src/app/(main)/(pages)/home/page.tsx @@ -1,3 +1,4 @@ +import { Greeting } from '@/components/Greeting'; import { MyCourses } from '@/components/MyCourses'; import { Redirect } from '@/components/Redirect'; import SearchBar from '@/components/search/SearchBar'; @@ -10,22 +11,11 @@ export default async function MyCoursesPage() { return ; } - // Get the current hour - const currentHour = new Date().getHours(); - - // Determine the appropriate greeting based on the time of day - let greeting = 'Good Morning'; - if (currentHour >= 12 && currentHour < 18) { - greeting = 'Good Afternoon'; - } else if (currentHour >= 18 || currentHour < 5) { - greeting = 'Good Evening'; - } - return (

- {greeting} {session.user.name} + {session.user.name}

diff --git a/src/app/admin/add-course/page.tsx b/src/app/admin/add-course/page.tsx new file mode 100644 index 000000000..dace4c5a8 --- /dev/null +++ b/src/app/admin/add-course/page.tsx @@ -0,0 +1,348 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import axios from 'axios'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { toast } from 'sonner'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Cuboid, PackagePlus } from 'lucide-react'; +import { FaDiscord } from 'react-icons/fa'; + +const courseSchema = z.object({ + title: z.string().min(5, { + message: 'Title must be at least 5 characters long.', + }), + imageUrl: z.string().url({ + message: 'Invalid URL format for imageUrl.', + }), + description: z.string().min(8, { + message: 'Description must be at least of 8 characters long.', + }), + slug: z.string(), + id: z.string(), + adminSecret: z.string(), + appxCourseId: z.string(), + discordRoleId: z.string(), +}); + +export default function Courses() { + const [isLoading, setIsLoading] = useState(false); + const router = useRouter(); + const [email, setEmail] = useState(''); + const [adminPassword, setAdminPassword] = useState(''); + + const form = useForm>({ + resolver: zodResolver(courseSchema), + defaultValues: { + title: '', + imageUrl: '', + description: '', + slug: '', + id: '', + adminSecret: '', + appxCourseId: '', + discordRoleId: '', + }, + }); + + const onSubmit = async (data: z.infer) => { + setIsLoading(true); + try { + await axios.post('/api/admin/course', data); + toast('course succesfully created'); + router.push('/'); + } catch (error: any) { + console.log(error); + toast(error.message); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ +
+ +

View Content

+
+ + + + +
+ New course +
+
+ +
+
Create new course for 100xdevs community and let user explore new courses
+
+ + + {/* Create a new course */} + Fill in the course details below + + +
+ + ( + + Title + + + + + + )} + /> + ( + + Image url + + + + + + )} + /> + ( + + Description + +