File size: 6,884 Bytes
5ccfcd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3720e61
5ccfcd1
 
 
 
 
 
 
 
 
 
 
 
3720e61
 
5ccfcd1
 
 
 
 
 
 
 
 
 
 
 
 
3720e61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ccfcd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import React from "react";
import { 
  FileText, 
  Plus, 
  Search, 
  X, 
  Trash2, 
  Wifi, 
  WifiOff, 
  RefreshCw 
} from "lucide-react";
import { Document } from "../../types";

interface DocumentSidebarProps {
  documents: Document[];
  activeDocId: string;
  setActiveDocId: (id: string) => void;
  setShowDocModal: (show: boolean) => void;
  handleDeleteDocument: (id: string, e: React.MouseEvent) => void;
  documentSearchTerm: string;
  setDocumentSearchTerm: (term: string) => void;
  syncStatus: "synced" | "syncing" | "offline" | "error";
  teamChangesEnabled: boolean;
  activeCollaborators: number;
  onClose?: () => void;
}

export default function DocumentSidebar({
  documents,
  activeDocId,
  setActiveDocId,
  setShowDocModal,
  handleDeleteDocument,
  documentSearchTerm,
  setDocumentSearchTerm,
  syncStatus,
  teamChangesEnabled,
  activeCollaborators,
  onClose
}: DocumentSidebarProps) {
  
  const filtered = documents.filter((doc) =>
    doc.title.toLowerCase().includes(documentSearchTerm.toLowerCase())
  );

  return (
    <div className="bg-[#15181E] border border-slate-800 rounded-xl p-4 space-y-3.5 shadow-md">
      
      <div className="flex items-center justify-between">
        <h2 className="text-xs font-mono uppercase tracking-wider text-slate-505 flex items-center gap-1.5 font-bold">
          <FileText className="h-4 w-4 text-blue-500" /> Folder Documents
        </h2>
        <div className="flex items-center gap-1.5">
          <button
            onClick={() => setShowDocModal(true)}
            className="p-1 px-2.5 bg-[#0F1115] hover:bg-slate-800 text-slate-300 hover:text-white border border-slate-800 rounded-md text-xs font-semibold flex items-center gap-1 transition cursor-pointer"
          >
            <Plus className="h-3 w-3" /> New
          </button>
          {onClose && (
            <button
              onClick={onClose}
              className="p-1 bg-[#0F1115] hover:bg-slate-800 text-slate-400 hover:text-white border border-slate-800 rounded-md transition cursor-pointer"
              title="Hide Sidebar"
            >
              <X className="h-3 w-3" />
            </button>
          )}
        </div>
      </div>

      {/* Highly intuitive search input layout */}
      <div className="relative">
        <span className="absolute inset-y-0 left-0 flex items-center pl-2.5 pointer-events-none">
          <Search className="h-3.5 w-3.5 text-slate-500" />
        </span>
        <input
          type="text"
          placeholder="Search by title..."
          value={documentSearchTerm}
          onChange={(e) => setDocumentSearchTerm(e.target.value)}
          className="w-full text-xs bg-[#0F1115] border border-slate-800 rounded-lg pl-8 pr-7 py-2 text-slate-205 placeholder:text-slate-650 focus:outline-none focus:border-slate-700 tracking-tight transition-colors"
        />
        {documentSearchTerm && (
          <button
            type="button"
            onClick={() => setDocumentSearchTerm("")}
            className="absolute inset-y-0 right-0 flex items-center pr-2.5 text-slate-500 hover:text-white transition-colors cursor-pointer"
            title="Clear search query"
          >
            <X className="h-3.5 w-3.5" />
          </button>
        )}
      </div>

      {/* Documents List */}
      <div className="space-y-1 max-h-[180px] overflow-y-auto pr-1">
        {filtered.length === 0 ? (
          <div className="text-center py-6 text-slate-500 text-xs font-mono">
            No matching segments
          </div>
        ) : (
          filtered.map((doc) => {
            const isSelected = doc.id === activeDocId;
            return (
              <div
                key={doc.id}
                onClick={() => setActiveDocId(doc.id)}
                className={`w-full text-left px-3 py-2 rounded-lg transition cursor-pointer flex items-center justify-between gap-2 group ${
                  isSelected
                    ? "bg-blue-600/10 border border-blue-500/20 text-white"
                    : "text-slate-400 hover:text-slate-200 hover:bg-[#0F1115]/50 border border-transparent"
                }`}
              >
                <div className="font-mono text-xs truncate max-w-[80%]">
                  {doc.title}
                </div>
                <button
                  onClick={(e) => handleDeleteDocument(doc.id, e)}
                  className="opacity-0 group-hover:opacity-100 hover:text-red-400 transition p-1 cursor-pointer"
                >
                  <Trash2 className="h-3 w-3" />
                </button>
              </div>
            );
          })
        )}
      </div>

      {/* Secure Offline Persistent Sync Status Banner */}
      <div className="pt-3 border-t border-slate-800 space-y-2.5">
        <div className="flex items-center justify-between text-xs">
          <span className="font-mono text-[10px] text-slate-500">Sync Pipeline</span>
          
          {syncStatus === "synced" && (
            <span className="flex items-center gap-1 font-mono text-[10px] text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded-full border border-emerald-500/20 text-[9px] font-bold">
              <Wifi className="h-3 w-3" /> Online Synced
            </span>
          )}
          {syncStatus === "syncing" && (
            <span className="flex items-center gap-1 font-mono text-[10px] text-amber-400 bg-amber-500/10 px-2 py-0.5 rounded-full border border-amber-500/20 text-[9px] font-bold">
              <RefreshCw className="h-3 w-3 animate-spin" /> Uploading Segment
            </span>
          )}
          {syncStatus === "offline" && (
            <span className="flex items-center gap-1 font-mono text-[10px] text-slate-400 bg-slate-800/10 px-2 py-0.5 rounded-full border border-slate-750 text-[9px] font-bold">
              <WifiOff className="h-3 w-3" /> Offline Buffer
            </span>
          )}
          {syncStatus === "error" && (
            <span className="flex items-center gap-1 font-mono text-[10px] text-red-405 bg-red-500/10 px-2 py-0.5 rounded-full border border-red-500/20 text-[9px] font-bold">
              <WifiOff className="h-3 w-3" /> Sync Failed
            </span>
          )}
        </div>

        <div className="bg-[#0F1115]/60 border border-slate-800 p-2.5 rounded-lg space-y-1">
          <div className="flex justify-between items-center text-[9px] text-slate-500 font-mono">
            <span>Collaborators</span>
            <span>{teamChangesEnabled ? activeCollaborators : "1"} Editor Online</span>
          </div>
          <div className="flex items-center gap-1 py-1">
            <div className="h-2 w-2 rounded-full bg-emerald-500 animate-pulse" />
            <span className="font-mono text-[9px] text-slate-400">Sarah and Liam joined review panel</span>
          </div>
        </div>
      </div>

    </div>
  );
}