Skip to content
Open
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
110 changes: 55 additions & 55 deletions src/components/cms/VersionControl.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
'use client';

import React from 'react';
import React, { useMemo } from 'react';
import { useCMS } from '@/hooks/useCMS';
import { History, RotateCcw, Clock, CheckCircle2 } from 'lucide-react';

export const VersionControl: React.FC = () => {
const { history, historyIndex, undo, redo, course } = useCMS();
const reversedHistory = useMemo(() => history.slice().reverse(), [history]);

return (
<div className="flex flex-col h-full bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
Expand All @@ -21,68 +22,67 @@ export const VersionControl: React.FC = () => {
<div className="absolute left-4 top-0 bottom-0 w-0.5 bg-gray-200 dark:bg-gray-700" />

<div className="space-y-8 relative">
{history
.map((snapshot, index) => {
const isCurrent = index === historyIndex;
const isPast = index < historyIndex;
{reversedHistory.map((snapshot, reversedIndex) => {
const originalIndex = history.length - 1 - reversedIndex;
const isCurrent = originalIndex === historyIndex;
const isPast = originalIndex < historyIndex;

return (
<div key={index} className="flex gap-4 items-start pl-2">
<div
className={`z-10 w-4 h-4 rounded-full mt-1.5 border-2 ${
isCurrent
? 'bg-blue-500 border-blue-200 dark:border-blue-800 animate-pulse'
: isPast
return (
<div key={snapshot.historyId} className="flex gap-4 items-start pl-2">
<div
className={`z-10 w-4 h-4 rounded-full mt-1.5 border-2 ${
isCurrent
? 'bg-blue-500 border-blue-200 dark:border-blue-800 animate-pulse'
: isPast
? 'bg-green-500 border-green-200 dark:border-green-800'
: 'bg-gray-300 border-white dark:border-gray-800'
}`}
/>
}`}
/>

<div
className={`flex-1 p-3 rounded-lg border transition-all ${
isCurrent
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
: 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-gray-300'
}`}
>
<div className="flex justify-between items-start mb-1">
<div className="text-sm font-semibold text-gray-800 dark:text-white">
{isCurrent ? 'Current Version' : `Version ${index + 1}`}
</div>
<div className="text-[10px] text-gray-500 flex items-center gap-1">
<Clock className="w-3 h-3" />
Just now
</div>
<div
className={`flex-1 p-3 rounded-lg border transition-all ${
isCurrent
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
: 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-gray-300'
}`}
>
<div className="flex justify-between items-start mb-1">
<div className="text-sm font-semibold text-gray-800 dark:text-white">
{isCurrent ? 'Current Version' : `Version ${originalIndex + 1}`}
</div>
<p className="text-xs text-gray-500 dark:text-gray-400">
{snapshot.modules.length} modules,{' '}
{snapshot.modules.reduce((acc, m) => acc + m.lessons.length, 0)} lessons
</p>
<div className="text-[10px] text-gray-500 flex items-center gap-1">
<Clock className="w-3 h-3" />
Just now
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400">
{snapshot.modules.length} modules,{' '}
{snapshot.modules.reduce((acc, m) => acc + m.lessons.length, 0)} lessons
</p>

{!isCurrent && (
<button
onClick={() => {
// Logic to jump to this specific history index
// In a full implementation, the store would handle this
}}
className="mt-2 text-[10px] font-medium text-blue-600 hover:text-blue-700 flex items-center gap-1"
>
<RotateCcw className="w-3 h-3" />
Preview & Rollback
</button>
)}
{!isCurrent && (
<button
onClick={() => {
// Logic to jump to this specific history index
// In a full implementation, the store would handle this
}}
className="mt-2 text-[10px] font-medium text-blue-600 hover:text-blue-700 flex items-center gap-1"
>
<RotateCcw className="w-3 h-3" />
Preview & Rollback
</button>
)}

{isCurrent && (
<div className="mt-2 text-[10px] font-medium text-green-600 flex items-center gap-1">
<CheckCircle2 className="w-3 h-3" />
Active
</div>
)}
</div>
{isCurrent && (
<div className="mt-2 text-[10px] font-medium text-green-600 flex items-center gap-1">
<CheckCircle2 className="w-3 h-3" />
Active
</div>
)}
</div>
);
})
.reverse()}{' '}
</div>
);
})}{' '}
{/* Show newest first */}
</div>
</div>
Expand Down
13 changes: 10 additions & 3 deletions src/store/cmsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { CMSCourse, MediaUploadTask, ContentTemplate } from '../types/cms';

type CMSHistoryEntry = CMSCourse & { historyId: string };

interface CMSState {
course: CMSCourse;
history: CMSCourse[];
history: CMSHistoryEntry[];
historyIndex: number;
mediaQueue: MediaUploadTask[];
templates: ContentTemplate[];
Expand All @@ -30,6 +32,11 @@ interface CMSState {
setTemplates: (templates: ContentTemplate[]) => void;
}

const createHistoryEntry = (course: CMSCourse): CMSHistoryEntry => ({
...course,
historyId: `history-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
});

export const useCMSStore = create<CMSState>()(
persist(
(set) => ({
Expand All @@ -48,7 +55,7 @@ export const useCMSStore = create<CMSState>()(
setCourse: (course) => {
set((state) => {
let newHistory = state.history.slice(0, state.historyIndex + 1);
newHistory.push(course);
newHistory.push(createHistoryEntry(course));

if (newHistory.length > 20) {
newHistory = newHistory.slice(newHistory.length - 20);
Expand All @@ -67,7 +74,7 @@ export const useCMSStore = create<CMSState>()(
const updatedCourse = { ...state.course, ...updates };
let newHistory = state.history.slice(0, state.historyIndex + 1);

newHistory.push(updatedCourse);
newHistory.push(createHistoryEntry(updatedCourse));

if (newHistory.length > 20) {
newHistory = newHistory.slice(newHistory.length - 20);
Expand Down
Loading