diff --git a/.prettierrc b/.prettierrc index 1a9abe1c7..68a46e1f4 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - "semi": false, + "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "all" diff --git a/src/actions/comment/index.ts b/src/actions/comment/index.ts index 9b4fe8406..63fc8921e 100644 --- a/src/actions/comment/index.ts +++ b/src/actions/comment/index.ts @@ -1,9 +1,11 @@ 'use server'; import { getServerSession } from 'next-auth'; import { + InputTypeApproveIntroComment, InputTypeCreateComment, InputTypeDeleteComment, InputTypeUpdateComment, + ReturnTypeApproveIntroComment, ReturnTypeCreateComment, ReturnTypeDeleteComment, ReturnTypeUpdateComment, @@ -12,6 +14,7 @@ import { authOptions } from '@/lib/auth'; import { rateLimit } from '@/lib/utils'; import prisma from '@/db'; import { + CommentApproveIntroSchema, CommentDeleteSchema, CommentInsertSchema, CommentUpdateSchema, @@ -74,7 +77,7 @@ const parseIntroComment = ( const lastLineParts = lines[lines.length - 1] .split('-') .map((part) => part.trim()); - if (lastLineParts.length < 2) { + if (lastLineParts.length < 3) { return null; } const timePattern = /(\d{2}):(\d{2})/; @@ -154,6 +157,15 @@ const createCommentHandler = async ( | null = []; if (data.content.startsWith('intro:')) { introData = parseIntroComment(data.content); + if ( + !introData || + introData.length === 0 || + introData[introData.length - 1].end === 0 + ) { + throw new Error( + 'Invalid intro comment format, remember to include end time on the segment. Example: 12:24- 23:43 - Introduction to the course', + ); + } // Here you might want to store introData in a specific way, depending on your needs } @@ -177,11 +189,12 @@ const createCommentHandler = async ( }); }); } - revalidatePath(data.currentPath); + if (data.currentPath) { + revalidatePath(data.currentPath); + } return { data: comment }; - } catch (error) { - console.log('error', error); - return { error: 'Failed to create comment.' }; + } catch (error: any) { + return { error: error.message || 'Failed to create comment.' }; } }; const updateCommentHandler = async ( @@ -222,12 +235,70 @@ const updateCommentHandler = async ( where: { id: commentId }, data: updObj, }); - revalidatePath(data.currentPath); + if (data.currentPath) { + revalidatePath(data.currentPath); + } return { data: updatedComment }; } catch (error) { return { error: 'Failed to update comment.' }; } }; +const approveIntroCommentHandler = async ( + data: InputTypeApproveIntroComment, +): Promise => { + const { content_comment_ids, approved, adminPassword } = data; + + if (adminPassword !== process.env.ADMIN_SECRET) { + return { error: 'Unauthorized' }; + } + + const [contentId, commentId] = content_comment_ids.split(';'); + try { + const existingComment = await prisma.comment.findUnique({ + where: { id: parseInt(commentId, 10) }, + }); + + if (!existingComment) { + return { error: 'Comment not found.' }; + } + + const introData = parseIntroComment(existingComment.content); + + if ( + !introData || + introData.length === 0 || + existingComment.commentType !== CommentType.INTRO + ) { + return { + error: + 'Comment is not an intro comment or can not be parsed. Plese check that last segment has end time include.', + }; + } + // Update the comment but if its admin we need to check if the comment is approved + const updObj = { + approved, + }; + let updatedComment = null; + await prisma.$transaction(async (prisma) => { + updatedComment = await prisma.comment.update({ + where: { id: parseInt(commentId, 10) }, + data: updObj, + }); + await prisma.videoMetadata.update({ + where: { + contentId: Number(contentId), + }, + data: { + segments: introData, + }, + }); + }); + + return { data: updatedComment! }; + } catch (error) { + return { error: 'Failed to update comment.' }; + } +}; const deleteCommentHandler = async ( data: InputTypeDeleteComment, @@ -279,8 +350,12 @@ const deleteCommentHandler = async ( where: { id: commentId }, }); }); - revalidatePath(data.currentPath); - return { data: { message: 'Comment and its replies deleted successfully' } }; + if (data.currentPath) { + revalidatePath(data.currentPath); + } + return { + data: { message: 'Comment and its replies deleted successfully' }, + }; } catch (error) { return { error: 'Failed to delete comment.' }; } @@ -298,3 +373,7 @@ export const deleteMessage = createSafeAction( CommentDeleteSchema, deleteCommentHandler, ); +export const approveComment = createSafeAction( + CommentApproveIntroSchema, + approveIntroCommentHandler, +); diff --git a/src/actions/comment/schema.ts b/src/actions/comment/schema.ts index a2d9070b6..34163be1e 100644 --- a/src/actions/comment/schema.ts +++ b/src/actions/comment/schema.ts @@ -4,7 +4,7 @@ export const CommentInsertSchema = z.object({ content: z.string().min(1, 'Comment content is required'), contentId: z.number(), parentId: z.number().optional(), - currentPath: z.string(), + currentPath: z.string().optional(), }); export const CommentUpdateSchema = z.object({ @@ -14,11 +14,15 @@ export const CommentUpdateSchema = z.object({ // downVotes: z.number().optional(), approved: z.boolean().optional(), adminPassword: z.string().optional(), - currentPath: z.string(), + currentPath: z.string().optional(), +}); +export const CommentApproveIntroSchema = z.object({ + content_comment_ids: z.string(), + approved: z.boolean().optional(), + adminPassword: z.string().optional(), }); - export const CommentDeleteSchema = z.object({ adminPassword: z.string().optional(), commentId: z.number(), - currentPath: z.string(), + currentPath: z.string().optional(), }); diff --git a/src/actions/comment/types.ts b/src/actions/comment/types.ts index fd164fa11..1c2ff6339 100644 --- a/src/actions/comment/types.ts +++ b/src/actions/comment/types.ts @@ -4,28 +4,36 @@ import { CommentInsertSchema, CommentUpdateSchema, CommentDeleteSchema, + CommentApproveIntroSchema, } from './schema'; import { Delete } from '../types'; import { User, Comment } from '@prisma/client'; -export type InputTypeCreateComment = z.infer +export type InputTypeCreateComment = z.infer; export type ReturnTypeCreateComment = ActionState< InputTypeCreateComment, Comment -> +>; -export type InputTypeUpdateComment = z.infer +export type InputTypeUpdateComment = z.infer; export type ReturnTypeUpdateComment = ActionState< InputTypeUpdateComment, Comment -> +>; +export type InputTypeApproveIntroComment = z.infer< + typeof CommentApproveIntroSchema +>; +export type ReturnTypeApproveIntroComment = ActionState< + InputTypeApproveIntroComment, + Comment +>; -export type InputTypeDeleteComment = z.infer +export type InputTypeDeleteComment = z.infer; export type ReturnTypeDeleteComment = ActionState< InputTypeDeleteComment, Delete -> +>; export interface ExtendedComment extends Comment { - user: User + user: User; } diff --git a/src/actions/commentVote/index.ts b/src/actions/commentVote/index.ts index 1d99b66ef..34530dcfb 100644 --- a/src/actions/commentVote/index.ts +++ b/src/actions/commentVote/index.ts @@ -113,4 +113,7 @@ const voteHandler = async ( } }; -export const voteHandlerAction = createSafeAction(VoteHandleSchema, voteHandler); +export const voteHandlerAction = createSafeAction( + VoteHandleSchema, + voteHandler, +); diff --git a/src/actions/commentVote/types.ts b/src/actions/commentVote/types.ts index 96ab5ebf8..a25a71024 100644 --- a/src/actions/commentVote/types.ts +++ b/src/actions/commentVote/types.ts @@ -3,8 +3,8 @@ import { ActionState } from '@/lib/create-safe-action'; import { z } from 'zod'; import { VoteHandleSchema } from './schema'; -export type InputTypeHandleVote = z.infer +export type InputTypeHandleVote = z.infer; export type ReturnTypeHandleVote = ActionState< InputTypeHandleVote, Comment | null -> +>; diff --git a/src/actions/types.ts b/src/actions/types.ts index cc5e84840..1570de0fc 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -1,16 +1,16 @@ import { CommentType } from '@prisma/client'; export interface QueryParams { - limit?: number - page?: number - commentfilter?: CommentFilter - search?: string - date?: string - type?: CommentType - parentId?: number - userId?: number - commentId?: number - timestamp?: number + limit?: number; + page?: number; + commentfilter?: CommentFilter; + search?: string; + date?: string; + type?: CommentType; + parentId?: number; + userId?: number; + commentId?: number; + timestamp?: number; } export enum CommentFilter { md = 'Most downvotes', @@ -18,5 +18,5 @@ export enum CommentFilter { mr = 'Most Recent', } export type Delete = { - message: string -} + message: string; +}; diff --git a/src/app/admin/comment/ApproveComment.tsx b/src/app/admin/comment/ApproveComment.tsx new file mode 100644 index 000000000..bee046f8d --- /dev/null +++ b/src/app/admin/comment/ApproveComment.tsx @@ -0,0 +1,79 @@ +'use client'; +import { approveComment } from '@/actions/comment'; +import { FormErrors } from '@/components/FormError'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { useAction } from '@/hooks/useAction'; +import { Label } from '@radix-ui/react-dropdown-menu'; +import React from 'react'; +import { toast } from 'sonner'; + +const ApproveComment = () => { + const formRef = React.useRef(null); + const { execute, fieldErrors } = useAction(approveComment, { + onSuccess: () => { + toast('Comment added'); + formRef.current?.reset(); + }, + onError: (error) => { + toast.error(error); + }, + }); + + const handleApprove = (e: React.FormEvent) => { + e.preventDefault(); + const formData = new FormData(e.target as HTMLFormElement); + + const commentId = formData.get('commentId') as string; + const adminPassword = formData.get('adminPassword') as string; + execute({ + content_comment_ids: commentId, + adminPassword, + approved: true, + }); + }; + return ( +
+
+
+
Approve Intro comment
+
+ Enter the information below to approve the comment +
+
+ +
+ + + +
+
+ + + +
+
+ +
+
+
+ ); +}; + +export default ApproveComment; diff --git a/src/app/admin/comment/page.tsx b/src/app/admin/comment/page.tsx new file mode 100644 index 000000000..11f993f7d --- /dev/null +++ b/src/app/admin/comment/page.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ApproveComment from './ApproveComment'; + +const CommentAdminPage = () => { + return ( +
+ +
+ ); +}; + +export default CommentAdminPage; diff --git a/src/app/admin/content/[...courseId]/page.tsx b/src/app/admin/content/[...courseId]/page.tsx index 962f10812..c21da5cdf 100644 --- a/src/app/admin/content/[...courseId]/page.tsx +++ b/src/app/admin/content/[...courseId]/page.tsx @@ -1,11 +1,15 @@ -import { getCourse, getCourseContent, getCurrentContentType } from '@/db/course'; +import { + getCourse, + getCourseContent, + getCurrentContentType, +} from '@/db/course'; import { AddContent } from '@/components/admin/AddContent'; import { AdminCourseContent } from '@/components/admin/CourseContent'; export default async function UpdateCourseContent({ params, }: { - params: { courseId: string[] } + params: { courseId: string[] }; }) { const courseId = params.courseId[0]; const rest = params.courseId.slice(1); diff --git a/src/app/api/admin/content/route.ts b/src/app/api/admin/content/route.ts index c80506656..7f7c6a1f3 100644 --- a/src/app/api/admin/content/route.ts +++ b/src/app/api/admin/content/route.ts @@ -11,13 +11,13 @@ export const POST = async (req: NextRequest) => { metadata, adminPassword, }: { - type: 'video' | 'folder' | 'notion' - thumbnail: string - title: string - courseId: number - parentContentId: number - metadata: any - adminPassword: string + type: 'video' | 'folder' | 'notion'; + thumbnail: string; + title: string; + courseId: number; + parentContentId: number; + metadata: any; + adminPassword: string; } = await req.json(); if (adminPassword !== process.env.ADMIN_SECRET) { diff --git a/src/app/courses/[...courseId]/page.tsx b/src/app/courses/[...courseId]/page.tsx index 2685538a2..aeb91435a 100644 --- a/src/app/courses/[...courseId]/page.tsx +++ b/src/app/courses/[...courseId]/page.tsx @@ -53,8 +53,8 @@ export default async function Course({ params, searchParams, }: { - params: { courseId: string[] } - searchParams: QueryParams + params: { courseId: string[] }; + searchParams: QueryParams; }) { const courseId = params.courseId[0]; const rest = params.courseId.slice(1); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 32f935cdb..daecca454 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -29,7 +29,7 @@ export const metadata: Metadata = { export default function RootLayout({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { return ( diff --git a/src/components/3dcard.tsx b/src/components/3dcard.tsx index d93900b9b..e7af41af6 100644 --- a/src/components/3dcard.tsx +++ b/src/components/3dcard.tsx @@ -21,9 +21,9 @@ export const CardContainer = ({ className, containerClassName, }: { - children?: React.ReactNode - className?: string - containerClassName?: string + children?: React.ReactNode; + className?: string; + containerClassName?: string; }) => { const containerRef = useRef(null); const [isMouseEntered, setIsMouseEntered] = useState(false); @@ -82,8 +82,8 @@ export const CardBody = ({ children, className, }: { - children: React.ReactNode - className?: string + children: React.ReactNode; + className?: string; }) => { return (
{ const ref = useRef(null); const [isMouseEntered] = useMouseEnter(); diff --git a/src/components/ContentCard.tsx b/src/components/ContentCard.tsx index 4da8c2962..ffb9bac9d 100644 --- a/src/components/ContentCard.tsx +++ b/src/components/ContentCard.tsx @@ -8,12 +8,12 @@ export const ContentCard = ({ markAsCompleted, percentComplete, }: { - contentId?: number - image: string - title: string - onClick: () => void - markAsCompleted?: boolean - percentComplete?: number | null + contentId?: number; + image: string; + title: string; + onClick: () => void; + markAsCompleted?: boolean; + percentComplete?: number | null; }) => { return (
{ + const handleCopyClick = () => { + try { + navigator.clipboard.writeText(textToCopy); + toast.success('Copied to clipboard'); + } catch (error) { + toast.error('Failed to copy to clipboard'); + } + }; + + return ( +
+ +
+ ); +}; + +export default CopyToClipboard; diff --git a/src/components/CourseCard.tsx b/src/components/CourseCard.tsx index 592ef5d98..8fdaf8ee1 100644 --- a/src/components/CourseCard.tsx +++ b/src/components/CourseCard.tsx @@ -8,8 +8,8 @@ export const CourseCard = ({ course, onClick, }: { - course: Course - onClick: () => void + course: Course; + onClick: () => void; }) => { return (
{ return (
diff --git a/src/components/FolderView.tsx b/src/components/FolderView.tsx index c55e67f64..5996319dc 100644 --- a/src/components/FolderView.tsx +++ b/src/components/FolderView.tsx @@ -7,15 +7,15 @@ export const FolderView = ({ courseId, rest, }: { - courseId: number - rest: string[] + courseId: number; + rest: string[]; courseContent: { - title: string - image: string - id: number - markAsCompleted: boolean - percentComplete: number | null - }[] + title: string; + image: string; + id: number; + markAsCompleted: boolean; + percentComplete: number | null; + }[]; }) => { const router = useRouter(); diff --git a/src/components/FormError.tsx b/src/components/FormError.tsx index 56bd1234e..715aa9124 100644 --- a/src/components/FormError.tsx +++ b/src/components/FormError.tsx @@ -1,8 +1,8 @@ import { XCircle } from 'lucide-react'; interface FormErrorsProps { - id: string - errors?: Record + id: string; + errors?: Record; } export const FormErrors = ({ id, errors }: FormErrorsProps) => { diff --git a/src/components/JoinDiscord.tsx b/src/components/JoinDiscord.tsx index 6c62f435d..b7a815415 100644 --- a/src/components/JoinDiscord.tsx +++ b/src/components/JoinDiscord.tsx @@ -10,8 +10,8 @@ export const JoinDiscord = ({ isNavigated = true, isInMenu = false, }: { - isNavigated?: boolean - isInMenu?: boolean + isNavigated?: boolean; + isInMenu?: boolean; }) => { if (isNavigated) { return ( diff --git a/src/components/Pagination.tsx b/src/components/Pagination.tsx index 39b7d7669..e2ede4305 100644 --- a/src/components/Pagination.tsx +++ b/src/components/Pagination.tsx @@ -9,7 +9,7 @@ import Link from 'next/link'; import { usePathname, useSearchParams } from 'next/navigation'; import React from 'react'; interface IPagination { - dataLength: number + dataLength: number; } const Pagination: React.FC = ({ dataLength = 1 }) => { const searchParams = useSearchParams(); diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 55c1d3cd1..48924a2d8 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -18,8 +18,8 @@ export function Sidebar({ courseId, fullCourseContent, }: { - fullCourseContent: Folder[] - courseId: string + fullCourseContent: Folder[]; + courseId: string; }) { const router = useRouter(); const [sidebarOpen, setSidebarOpen] = useRecoilState(sidebarOpenAtom); @@ -41,7 +41,11 @@ export function Sidebar({ return newPath; } if (content.children) { - const childPath = findPathToContent(content.children, targetId, newPath); + const childPath = findPathToContent( + content.children, + targetId, + newPath, + ); if (childPath) { return childPath; } @@ -134,8 +138,8 @@ export function ToggleButton({ onClick, sidebarOpen, }: { - onClick: () => void - sidebarOpen: boolean + onClick: () => void; + sidebarOpen: boolean; }) { return (
= ({ comment, diff --git a/src/components/landing/us-section/why-us-card/why-us-card.tsx b/src/components/landing/us-section/why-us-card/why-us-card.tsx index d506ab161..3cd3c4518 100644 --- a/src/components/landing/us-section/why-us-card/why-us-card.tsx +++ b/src/components/landing/us-section/why-us-card/why-us-card.tsx @@ -1,7 +1,7 @@ interface IWhyUsCard { - tagline: string - headline: string - description: string + tagline: string; + headline: string; + description: string; } const WhyUsCard = ({ tagline, headline, description }: IWhyUsCard) => { diff --git a/src/components/landing/us-section/why-us-card/why-us-content.ts b/src/components/landing/us-section/why-us-card/why-us-content.ts index 744a3cab2..96f986b7f 100644 --- a/src/components/landing/us-section/why-us-card/why-us-content.ts +++ b/src/components/landing/us-section/why-us-card/why-us-content.ts @@ -1,10 +1,10 @@ type TwhyUs = { - id: number - icon: any - tagline: string - headline: string - description: string -}[] + id: number; + icon: any; + tagline: string; + headline: string; + description: string; +}[]; export const whyUs: TwhyUs = [ { diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index db7055bbd..805cddaea 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -41,7 +41,7 @@ const buttonVariants = cva( export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean + asChild?: boolean; } const Button = React.forwardRef( diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index dcdb669c8..3028066c7 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -76,4 +76,11 @@ const CardFooter = React.forwardRef< )); CardFooter.displayName = 'CardFooter'; -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx index 06b218651..f3749b263 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/src/components/ui/dropdown-menu.tsx @@ -21,7 +21,7 @@ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, children, ...props }, ref) => ( , React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( , React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( & { - withHandle?: boolean + withHandle?: boolean; }) => ( +type ToasterProps = React.ComponentProps; const Toaster = ({ ...props }: ToasterProps) => { const { theme = 'system' } = useTheme(); diff --git a/src/db/Cache.ts b/src/db/Cache.ts index b8e451c0e..43d75261a 100644 --- a/src/db/Cache.ts +++ b/src/db/Cache.ts @@ -2,8 +2,8 @@ export class Cache { private inMemoryDb: Map< string, { - value: any - expiry: number + value: any; + expiry: number; } >; private static instance: Cache; @@ -12,8 +12,8 @@ export class Cache { this.inMemoryDb = new Map< string, { - value: any - expiry: number + value: any; + expiry: number; } >(); } diff --git a/src/db/course.ts b/src/db/course.ts index 2cefacb05..ab101461b 100644 --- a/src/db/course.ts +++ b/src/db/course.ts @@ -4,26 +4,26 @@ import { authOptions } from '@/lib/auth'; import { getServerSession } from 'next-auth'; export interface Content { - id: number - type: string - title: string - description: string | null - thumbnail: string | null - parentId: number | null - createdAt: string - children: Content[] + id: number; + type: string; + title: string; + description: string | null; + thumbnail: string | null; + parentId: number | null; + createdAt: string; + children: Content[]; videoProgress?: { - currentTimestamp: string - markAsCompleted?: boolean - } + currentTimestamp: string; + markAsCompleted?: boolean; + }; } export interface Folder extends Content { - type: 'folder' + type: 'folder'; } export interface Video extends Content { - type: 'video' + type: 'video'; } export async function getAllCourses() { @@ -42,17 +42,17 @@ export async function getAllCourses() { export async function getAllCoursesAndContentHierarchy(): Promise< { - id: number - title: string - description: string - appxCourseId: number - discordRoleId: string - slug: string - imageUrl: string - openToEveryone: boolean + id: number; + title: string; + description: string; + appxCourseId: number; + discordRoleId: string; + slug: string; + imageUrl: string; + openToEveryone: boolean; content: { - contentId: number - }[] + contentId: number; + }[]; }[] > { const value = await Cache.getInstance().get( @@ -90,15 +90,15 @@ export async function getAllCoursesAndContentHierarchy(): Promise< export async function getAllVideos(): Promise< { - id: number - type: string - title: string - hidden: boolean - description: string | null - thumbnail: string | null - parentId: number | null - createdAt: Date - notionMetadataId: number | null + id: number; + type: string; + title: string; + hidden: boolean; + description: string | null; + thumbnail: string | null; + parentId: number | null; + createdAt: Date; + notionMetadataId: number | null; }[] > { const value = await Cache.getInstance().get('getAllVideos', []); diff --git a/src/db/index.ts b/src/db/index.ts index 18386efa9..a4799dd31 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -4,11 +4,11 @@ const prismaClientSingleton = () => { return new PrismaClient(); }; -type PrismaClientSingleton = ReturnType +type PrismaClientSingleton = ReturnType; // eslint-disable-next-line const globalForPrisma = globalThis as unknown as { - prisma: PrismaClientSingleton | undefined + prisma: PrismaClientSingleton | undefined; }; const prisma = globalForPrisma.prisma ?? prismaClientSingleton(); diff --git a/src/hooks/useAction.ts b/src/hooks/useAction.ts index 2a065d605..9d5a315f0 100644 --- a/src/hooks/useAction.ts +++ b/src/hooks/useAction.ts @@ -6,12 +6,12 @@ import { ActionState, FieldErrors } from '@/lib/create-safe-action'; type Action = ( data: TInput, -) => Promise> +) => Promise>; interface UseActionOptions { - onSuccess?: (data: TOutput) => void - onError?: (error: string) => void - onComplete?: () => void + onSuccess?: (data: TOutput) => void; + onError?: (error: string) => void; + onComplete?: () => void; } export const useAction = ( diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 6c9ba2ed4..ee9f7f01e 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -8,9 +8,9 @@ async function validateUser( | { data: null } | { data: { - name: string - userid: string - } + name: string; + userid: string; + }; } > { if (process.env.LOCAL_CMS_PROVIDER) { diff --git a/src/lib/create-safe-action.ts b/src/lib/create-safe-action.ts index 98ad93a3f..adf7dcca7 100644 --- a/src/lib/create-safe-action.ts +++ b/src/lib/create-safe-action.ts @@ -7,14 +7,14 @@ import { z } from 'zod'; export type FieldErrors = { - [K in keyof T]?: string[] -} + [K in keyof T]?: string[]; +}; export type ActionState = { - fieldErrors?: FieldErrors - error?: string | null - data?: TOutput -} + fieldErrors?: FieldErrors; + error?: string | null; + data?: TOutput; +}; export const createSafeAction = ( schema: z.Schema, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e2441ba30..0f19afaf4 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -9,13 +9,13 @@ export function cn(...inputs: ClassValue[]) { } export interface VideoJsPlayer { - eme: () => void + eme: () => void; } export interface Segment { - start: number - end: number - title: string + start: number; + end: number; + title: string; } export const formatTime = (seconds: number): string => { const date = new Date(seconds * 1000); @@ -178,7 +178,7 @@ export const getFolderPercentCompleted = (childrenContent: any) => { }; interface RateLimiter { - timestamps: Date[] + timestamps: Date[]; } const userRateLimits = new Map(); const RATE_LIMIT_COUNT = 5; // Nums of comment s allowed in the interval @@ -249,9 +249,9 @@ export const paginationData = (searchParams: QueryParams) => { }; }; interface PaginationInfo { - pageNumber: number - pageSize: number - skip: number + pageNumber: number; + pageSize: number; + skip: number; } export const constructCommentPrismaQuery = ( diff --git a/src/store/atoms/courses.ts b/src/store/atoms/courses.ts index f0ba17431..415dc756c 100644 --- a/src/store/atoms/courses.ts +++ b/src/store/atoms/courses.ts @@ -2,17 +2,17 @@ import axios from 'axios'; import { atom, selector } from 'recoil'; export type Course = { - id: number - slug: string - title: string - description: string - imageUrl: string - appxCourseId: number - discordRoleId: string - openToEveryone: boolean - totalVideos?: number - totalVideosWatched?: number -} + id: number; + slug: string; + title: string; + description: string; + imageUrl: string; + appxCourseId: number; + discordRoleId: string; + openToEveryone: boolean; + totalVideos?: number; + totalVideosWatched?: number; +}; const coursesSelector = selector({ key: 'coursesSelector',