Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-pajak committed Dec 12, 2024
1 parent eefad3a commit 5b11f55
Show file tree
Hide file tree
Showing 29 changed files with 608 additions and 51 deletions.
27 changes: 24 additions & 3 deletions apps/api/src/courses/course.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,32 @@ export class CourseService {
};
}

// TODO: Needs to be refactored
async getTeacherCourses(authorId: UUIDType): Promise<AllCoursesForTeacherResponse> {
return this.db
.select(this.getSelectField())
.select({
id: courses.id,
description: sql<string>`${courses.description}`,
title: courses.title,
imageUrl: courses.imageUrl,
authorId: sql<string>`${courses.authorId}`,
author: sql<string>`CONCAT(${users.firstName} || ' ' || ${users.lastName})`,
authorEmail: sql<string>`${users.email}`,
category: sql<string>`${categories.title}`,
enrolled: sql<boolean>`CASE WHEN ${studentCourses.studentId} IS NOT NULL THEN true ELSE false END`,
enrolledParticipantCount: sql<number>`0`,
courseLessonCount: courses.lessonsCount,
completedLessonCount: sql<number>`0`,
priceInCents: courses.priceInCents,
currency: courses.currency,
hasFreeLessons: sql<boolean>`
EXISTS (
SELECT 1
FROM ${courseLessons}
WHERE ${courseLessons.courseId} = ${courses.id}
AND ${courseLessons.isFree} = true
)`,
})
.from(courses)
.leftJoin(studentCourses, eq(studentCourses.courseId, courses.id))
.leftJoin(categories, eq(courses.categoryId, categories.id))
Expand All @@ -545,8 +568,6 @@ export class CourseService {
users.email,
studentCourses.studentId,
categories.title,
coursesSummaryStats.freePurchasedCount,
coursesSummaryStats.paidPurchasedCount,
)
.orderBy(
sql<boolean>`CASE WHEN ${studentCourses.studentId} IS NULL THEN TRUE ELSE FALSE END`,
Expand Down
22 changes: 22 additions & 0 deletions apps/api/src/swagger/api-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2302,6 +2302,26 @@
"data": {
"type": "object",
"properties": {
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"id": {
"format": "uuid",
"type": "string"
Expand Down Expand Up @@ -2348,6 +2368,8 @@
}
},
"required": [
"firstName",
"lastName",
"id",
"description",
"contactEmail",
Expand Down
4 changes: 3 additions & 1 deletion apps/api/src/users/schemas/user.schema.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Type, type Static } from "@sinclair/typebox";
import { type Static, Type } from "@sinclair/typebox";

import { commonUserSchema } from "src/common/schemas/common-user.schema";

export const allUsersSchema = Type.Array(commonUserSchema);
export const userDetailsSchema = Type.Object({
firstName: Type.Union([Type.String(), Type.Null()]),
lastName: Type.Union([Type.String(), Type.Null()]),
id: Type.String({ format: "uuid" }),
description: Type.Union([Type.String(), Type.Null()]),
contactEmail: Type.Union([Type.String(), Type.Null()]),
Expand Down
3 changes: 3 additions & 0 deletions apps/api/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,16 @@ export class UsersService {
public async getUserDetails(userId: string): Promise<UserDetails> {
const [userBio]: UserDetails[] = await this.db
.select({
firstName: users.firstName,
lastName: users.lastName,
id: userDetails.id,
description: userDetails.description,
contactEmail: userDetails.contactEmail,
contactPhone: userDetails.contactPhoneNumber,
jobTitle: userDetails.jobTitle,
})
.from(userDetails)
.leftJoin(users, eq(userDetails.userId, users.id))
.where(eq(userDetails.userId, userId));

if (!userBio) {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/api/queries/useTeacherCourses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ApiClient } from "../api-client";

import type { GetTeacherCoursesResponse } from "../generated-api";

export const teacherCourses = (authorId: string) => {
export const teacherCoursesOptions = (authorId: string) => {
return {
queryKey: ["teacher-courses", authorId],
queryFn: async () => {
Expand All @@ -17,5 +17,5 @@ export const teacherCourses = (authorId: string) => {
};

export function useTeacherCourses(authorId: string) {
return useQuery(teacherCourses(authorId));
return useQuery(teacherCoursesOptions(authorId));
}
6 changes: 2 additions & 4 deletions apps/web/app/assets/svgs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ export { default as ArrowUp } from "./arrow-up.svg?react";
export { default as ArrowDown } from "./arrow-down.svg?react";
export { default as DragAndDropIcon } from "./drag-and-drop.svg?react";
export { default as Info } from "./info.svg?react";
export { default as Text } from "./text.svg?react";
export { default as Presentation } from "./presentation.svg?react";
export { default as Video } from "./video.svg?react";
export { default as Quiz } from "./quiz.svg?react";
export { default as Warning } from "./warning.svg?react";
export { default as Admin } from "./admin.svg?react";

export * from "./lesson-types";
4 changes: 4 additions & 0 deletions apps/web/app/assets/svgs/lesson-types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as Text } from "./text.svg?react";
export { default as Presentation } from "./presentation.svg?react";
export { default as Video } from "./video.svg?react";
export { default as Quiz } from "./quiz.svg?react";
3 changes: 3 additions & 0 deletions apps/web/app/assets/svgs/lesson-types/presentation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/web/app/assets/svgs/lesson-types/quiz.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/web/app/assets/svgs/lesson-types/text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/web/app/assets/svgs/lesson-types/video.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions apps/web/app/assets/svgs/presentation.svg

This file was deleted.

3 changes: 0 additions & 3 deletions apps/web/app/assets/svgs/quiz.svg

This file was deleted.

3 changes: 0 additions & 3 deletions apps/web/app/assets/svgs/text.svg

This file was deleted.

3 changes: 0 additions & 3 deletions apps/web/app/assets/svgs/video.svg

This file was deleted.

44 changes: 44 additions & 0 deletions apps/web/app/components/Badges/ProgressBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Badge } from "~/components/ui/badge";

import type { IconName } from "~/types/shared";

type ProgressBadgeProps = {
progress: "completed" | "inProgress" | "notStarted";
className?: string;
};

type ProgressConfig = {
[key in "completed" | "inProgress" | "notStarted"]: {
variant: "successFilled" | "inProgressFilled" | "notStartedFilled";
icon: IconName;
label: string;
};
};

export const ProgressBadge = ({ progress, className }: ProgressBadgeProps) => {
const progressConfig: ProgressConfig = {
completed: {
variant: "successFilled",
icon: "InputRoundedMarkerSuccess",
label: "Completed",
},
inProgress: {
variant: "inProgressFilled",
icon: "InProgress",
label: "In Progress",
},
notStarted: {
variant: "notStartedFilled",
icon: "NotStartedRounded",
label: "Not Started",
},
};

const { variant, icon, label } = progressConfig[progress];

return (
<Badge variant={variant} icon={icon} {...(Boolean(className) && { className })}>
{label}
</Badge>
);
};
1 change: 1 addition & 0 deletions apps/web/app/components/CardBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const badgeVariants = cva(
default: "text-neutral-900",
primary: "text-primary-950",
secondary: "text-secondary-700",
secondaryFilled: "text-secondary-700 bg-secondary-50",
successOutlined: "text-success-800",
successFilled: "text-white bg-success-600",
},
Expand Down
46 changes: 42 additions & 4 deletions apps/web/app/components/PageWrapper/PageWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
import {
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from "~/components/ui/breadcrumb";
import { cn } from "~/lib/utils";

import type { HTMLAttributes } from "react";
import type { HTMLAttributes, ReactNode } from "react";

type PageWrapperProps = HTMLAttributes<HTMLDivElement> & {
breadcrumbs?: { title: string; href: string }[];
children: ReactNode;
className?: string;
};

export const PageWrapper = ({ className, ...props }: PageWrapperProps) => {
type Breadcrumb = { title: string; href: string };

type BreadcrumbsProps = {
breadcrumbs?: Breadcrumb[];
};

export const Breadcrumbs = ({ breadcrumbs = [] }: BreadcrumbsProps) => {
if (!breadcrumbs.length) return null;

return (
<BreadcrumbList>
{breadcrumbs.map(({ href, title }, index) => (
<BreadcrumbItem key={index}>
<BreadcrumbLink href={href}>{title}</BreadcrumbLink>
{index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}
</BreadcrumbItem>
))}
</BreadcrumbList>
);
};

export const PageWrapper = ({ className, breadcrumbs, children, ...props }: PageWrapperProps) => {
const hasBreadcrumbs = Boolean(breadcrumbs);

const classes = cn(
"h-auto w-full pt-6 px-4 pb-4 md:px-6 md:pb-6 2xl:pt-12 2xl:px-8 2xl:pb-8",
"w-full pt-6 px-4 pb-4 md:px-6 md:pb-6 3xl:pt-12 3xl:px-8 3xl:pb-8",
{ "pt-8 md:pt-6 3xl:pb-2": hasBreadcrumbs },
className,
);
return <div className={classes} {...props} />;

return (
<div className={classes} {...props}>
{breadcrumbs && <Breadcrumbs breadcrumbs={breadcrumbs} />}
{children}
</div>
);
};
52 changes: 52 additions & 0 deletions apps/web/app/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import * as AccordionPrimitive from "@radix-ui/react-accordion";
import * as React from "react";

import { cn } from "~/lib/utils";

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item ref={ref} className={cn(className)} {...props} />
));
AccordionItem.displayName = "AccordionItem";

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between font-medium transition-all",
className,
)}
{...props}
>
{children}
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn(className)}>{children}</div>
</AccordionPrimitive.Content>
));

AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
Loading

0 comments on commit 5b11f55

Please sign in to comment.