Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/common/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
DropdownMenuTrigger,
} from '../ui/DropdownMenu';
import { Avatar, AvatarFallback, AvatarImage } from '../ui/Avatar.tsx';
import RoleBadge from '../ui/RoleBadge.tsx';
import RoleBadge from '../ui/RoleBadge/RoleBadge.tsx';
import { useAuth } from '../../hooks/useAuth';
import Button from '../ui/Button/Button';
import { AdminControls } from './navBar/AdminControls.tsx';
Expand Down
2 changes: 1 addition & 1 deletion src/components/landing/TrendingCourses.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useTrendingCourses } from '../../hooks/useCourses.ts';
import CoursePreviewCard from '../ui/CoursePreviewCard.tsx';
import CoursePreviewCard from '../ui/CoursePreviewCard/CoursePreviewCard.tsx';
import { useState, useEffect, useMemo } from 'react';

export function TrendingCourses() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/professor/courseEditor/CourseHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Link, useNavigate } from 'react-router-dom';
import Button from '../../ui/Button/Button';
import SaveStatus from './SaveStatus';
import Switch from '../../ui/Switch';
import Switch from '../../ui/Swtich/Switch';
import { ArrowLeft, Edit, Eye, FileText, HelpCircle } from 'lucide-react';

interface CourseHeaderProps {
Expand Down
2 changes: 1 addition & 1 deletion src/components/professor/courseEditor/UnitContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
Eye,
Edit2,
} from 'lucide-react';
import DocumentViewer from '../../ui/DocumentViewer';
import DocumentViewer from '../../ui/DocumentViewer/DocumentViewer';
import UnitEditor from '../../landing/UnitEditor';
// import ActivityCard from '../../landing/professorCourseEdition/ActivityCard'; // Obsoleto, ahora usamos questions
import type { Block } from '@blocknote/core';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Dialog, DialogHeader, DialogTitle } from '../../../ui/Dialog';
import Input from '../../../ui/Input/Input';
import Label from '../../../ui/Label';
import Label from '../../../ui/Label/Label';
import Textarea from '../../../ui/TextArea/TextArea';
import Select from '../../../ui/Select';
import Switch from '../../../ui/Switch';
import Select from '../../../ui/Select/Select';
import Switch from '../../../ui/Swtich/Switch';
import Button from '../../../ui/Button/Button';

interface CourseConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useMemo } from 'react';
import { useForm } from 'react-hook-form';
import { valibotResolver } from '@hookform/resolvers/valibot';
import * as v from 'valibot';
import Modal from '../../../ui/Modal';
import Modal from '../../../ui/Modal/Modal';
import Button from '../../../ui/Button/Button';
import Input from '../../../ui/Input/Input';
import Textarea from '../../../ui/TextArea/TextArea';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { valibotResolver } from '@hookform/resolvers/valibot';
import * as v from 'valibot';
import Modal from '../../../ui/Modal';
import Modal from '../../../ui/Modal/Modal';
import Button from '../../../ui/Button/Button';
import Input from '../../../ui/Input/Input';
import Textarea from '../../../ui/TextArea/TextArea';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useMemo } from 'react';
import Modal from '../../../ui/Modal';
import Modal from '../../../ui/Modal/Modal';
import Button from '../../../ui/Button/Button';
import Input from '../../../ui/Input/Input';
import { Building2, Search, Users, AlertCircle, Send } from 'lucide-react';
Expand Down
2 changes: 1 addition & 1 deletion src/components/student/MaterialsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Material } from '../../types/entities';
import Button from '../ui/Button/Button';
import { Card, CardContent, CardHeader, CardTitle } from '../ui/Card';
import { useState } from 'react';
import DocumentViewer from '../ui/DocumentViewer';
import DocumentViewer from '../ui/DocumentViewer/DocumentViewer';

interface MaterialsListProps {
materials: Material[];
Expand Down
2 changes: 1 addition & 1 deletion src/components/student/StudentAppealsHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Dialog, DialogHeader, DialogTitle } from '../ui/Dialog';
import { FileText, Calendar, Eye, GraduationCap, ExternalLink } from 'lucide-react';
import { useMyAppeals } from '../../hooks/useAppeals';
import type { Appeal } from '../../types/entities';
import DocumentViewer from '../ui/DocumentViewer';
import DocumentViewer from '../ui/DocumentViewer/DocumentViewer';

export default function StudentAppealsHistory() {
const { data: appeals, isLoading, isError } = useMyAppeals();
Expand Down
1 change: 0 additions & 1 deletion src/components/ui/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/react-vite';

import Button from './Button';
Expand Down
117 changes: 117 additions & 0 deletions src/components/ui/CoursePreviewCard/CoursePreviewCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import CoursePreviewCard from './CoursePreviewCard';
import { BrowserRouter } from 'react-router-dom';
import type { Course } from '../../../types/entities';

/**
* Mock data representing a Course.
* We use 'as unknown as Course' to satisfy strict TypeScript checks
* regarding deep nested interfaces (like Student[], Unit[], etc.)
* without having to mock every single property of the database schema.
*/
const mockCourse = {
id: 'course_123',
name: 'Introduction to React & Modern UI',
description: 'Learn how to build modern interfaces, reusable components, and manage global state with 2024 best practices.',
imageUrl: 'https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=1000&auto=format&fit=crop',
isFree: false,
priceInCents: 2999, // $29.99
status: 'published', // Mandatory field based on your interface

// Nested Objects Mocking
courseType: { id: 'type_1', name: 'Frontend Development' },

// Assuming Professor interface structure based on usage
professor: {
id: 'prof_1',
user: { name: 'Elena', surname: 'Torres' },
institution: { id: 'inst_1', name: 'Tech University' }
},

// Optional root institution
institution: { id: 'inst_1', name: 'Tech University' },

// Arrays
studentsCount: 342,
students: [],
units: [{}, {}, {}, {}, {}], // Dummy units for length count

createdAt: new Date(),
updatedAt: new Date(),
} as unknown as Course;

const meta = {
title: 'Components/ui/CoursePreviewCard',
component: CoursePreviewCard,
parameters: {
layout: 'centered',
},
decorators: [
(Story) => (
<BrowserRouter>
<div className="w-[350px]">
<Story />
</div>
</BrowserRouter>
),
],
tags: ['autodocs'],
argTypes: {
hideButton: { control: 'boolean', description: 'Hides the call-to-action button' },
hideInstructor: { control: 'boolean', description: 'Hides the instructor information' },
course: { control: 'object', description: 'Full course object' },
},
} satisfies Meta<typeof CoursePreviewCard>;

export default meta;
type Story = StoryObj<typeof CoursePreviewCard>

// --- Stories ---

export const Default: Story = {
args: {
course: mockCourse,
},
};

export const FreeCourse: Story = {
args: {
// We spread the mock and cast again to ensure it fits the Course type
course: {
...mockCourse,
name: 'Programming Fundamentals',
isFree: true,
priceInCents: 0,
} as unknown as Course,
},
};

export const CreationPreview: Story = {
args: {
// Individual props mode (No course object passed)
name: 'Draft Course Title',
description: 'This is a description being typed by the professor in real-time to preview the card appearance...',
imageUrl: 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?q=80&w=1000&auto=format&fit=crop',
isFree: false,
price: 45.50,
courseType: { id: '2', name: 'UX/UI Design' },
},
};

export const NoImage: Story = {
args: {
course: {
...mockCourse,
imageUrl: null as unknown as string, // Force null for visual test
name: 'Course without cover image',
} as unknown as Course,
},
};

export const Minimal: Story = {
args: {
course: mockCourse,
hideButton: true,
hideInstructor: true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {
CardHeader,
CardTitle,
CardDescription,
} from './Card';
import Badge from './Badge/Badge';
import Button from './Button/Button';
import type { Course, CourseType } from '../../types/entities';
import { cn } from '../../lib/utils';
} from '../Card';
import Badge from '../Badge/Badge';
import Button from '../Button/Button';
import type { Course, CourseType } from '../../../types/entities';
import { cn } from '../../../lib/utils';
import { Link } from 'react-router-dom';
import { formatCurrency } from '../../lib/currency';
import { formatCurrency } from '../../../lib/currency';

interface BasePreviewCardProps extends React.HTMLAttributes<HTMLDivElement> {
hideButton?: boolean;
Expand Down Expand Up @@ -42,6 +42,9 @@ interface IndividualPropsPreview extends BasePreviewCardProps {

type CoursePreviewCardProps = CourseObjectProps | IndividualPropsPreview;

/**
*This component displays a preview card for a course. It can accept either a full
*/
const CoursePreviewCard = React.forwardRef<HTMLDivElement, CoursePreviewCardProps>(
({ course, name, description, imageUrl, isFree, price, courseType, hideButton = false, hideInstructor = false, className, ...props }, ref) => {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import DocumentViewer from '../DocumentViewer';
import DocumentViewer from './DocumentViewer';

const meta = {
title: 'Components/ui/DocumentViewer',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ReactDOM from 'react-dom';
import { X } from 'lucide-react';
import Button from './Button/Button.tsx';
import Button from '../Button/Button.tsx';

interface DocumentViewerProps {
url: string;
Expand Down
18 changes: 18 additions & 0 deletions src/components/ui/Label/Label.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/react-vite';

import Label from './Label';

const meta = {
component: Label,
} satisfies Meta<typeof Label>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
children:"Label Component",
id: "label-story"
},
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { cn } from '../../lib/utils';
import { cn } from '../../../lib/utils';

/**
* @typedef {object} LabelProps
Expand Down
21 changes: 21 additions & 0 deletions src/components/ui/Modal/Modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Meta, StoryObj } from '@storybook/react-vite';

import Modal from './Modal';

const meta = {
component: Modal,
} satisfies Meta<typeof Modal>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
isOpen: true,
onClose: () => {},
title: "Modal Title",
children: <div className="p-4">This is the modal content.</div>,
size: 'lg',

},
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ interface ModalProps {
size?: 'sm' | 'md' | 'lg' | 'xl';
}

/**
* A reusable modal component with customizable size and close functionality.
*/
export default function Modal({ isOpen, onClose, title, children, size = 'lg' }: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);

Expand Down
20 changes: 20 additions & 0 deletions src/components/ui/ProfileField/ProfileField.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react-vite';

import ProfileField from './ProfileField';

const meta = {
component: ProfileField,
} satisfies Meta<typeof ProfileField>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
isEditing: false,
label: "User Name",
value: "",
icon: <span>👤</span>,

},
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from 'react';
import Input, { type InputProps } from './Input/Input';
import Input, { type InputProps } from '../Input/Input';

interface ProfileFieldProps extends InputProps {
isEditing: boolean;
icon: React.ReactNode;
}

/**
* A component that displays a profile field which can be either in view mode or edit mode.
*
* @param {ProfileFieldProps} props - The properties to configure the ProfileField component.
*/
const ProfileField = React.forwardRef<HTMLInputElement, ProfileFieldProps>(
({ isEditing, icon, label, value, ...props }, ref) => {
if (isEditing) {
Expand Down
28 changes: 28 additions & 0 deletions src/components/ui/RoleBadge/RoleBadge.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import RoleBadge from './RoleBadge';

const meta = {
component: RoleBadge,
} satisfies Meta<typeof RoleBadge>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Professor: Story = {
args: {
role: 'professor',
},
};

export const Student: Story = {
args: {
role: 'student',
},
};

export const Admin: Story = {
args: {
role: 'admin',
},
};

Loading