Skip to content

Commit 952d0d6

Browse files
committed
Merge branch 'feature/fe-document-archive' of github.com:AET-DevOps25/team-oopsops into feature/fe-document-archive
2 parents cb55d8d + e3f4c1c commit 952d0d6

File tree

63 files changed

+1722
-238
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1722
-238
lines changed

.github/workflows/deploy-to-rancher.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ jobs:
3939
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
4040
4141
- name: Ensure namespace exists
42-
run: kubectl get ns oops-ops || kubectl create ns oops-ops
42+
run: kubectl get ns oopsops-test || kubectl create ns oopsops-test
4343

4444
- name: Create OpenAI API key secret
4545
run: |
4646
kubectl create secret generic openai-secret \
47-
--namespace oops-ops \
47+
--namespace oopsops-test \
4848
--from-literal=OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }} \
4949
--dry-run=client -o yaml | kubectl apply -f -
5050
5151
- name: Deploy using Helm
5252
run: |
5353
helm upgrade --install oopsops-app ./helm/oopsops \
54-
--namespace oops-ops
54+
--namespace oopsops-test

.github/workflows/deploy-to-test.yaml

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Deploy to Rancher Test (PR branches)
33
on:
44
push:
55
branches:
6-
- '**'
6+
- "**"
77

88
env:
99
REPO_OWNER: aet-devops25
@@ -21,15 +21,15 @@ env:
2121
jobs:
2222
test-genai-service:
2323
runs-on: ubuntu-latest
24-
24+
2525
steps:
2626
- name: Checkout code
2727
uses: actions/checkout@v4
2828

2929
- name: Set up Python 3.11
3030
uses: actions/setup-python@v5
3131
with:
32-
python-version: '3.11'
32+
python-version: "3.11"
3333

3434
- name: Cache pip dependencies
3535
uses: actions/cache@v4
@@ -87,7 +87,7 @@ jobs:
8787
docker build -t ${{ env.DOCUMENT_IMAGE }}:$TAG -t ${{ env.DOCUMENT_IMAGE }}:latest -f ${{ env.DOCUMENT_DIR }}/Dockerfile ./server
8888
docker push ${{ env.DOCUMENT_IMAGE }}:$TAG
8989
docker push ${{ env.DOCUMENT_IMAGE }}:latest
90-
90+
9191
- name: Build and Push Anonymization Service Image
9292
run: |
9393
docker build -t ${{ env.ANONYMIZATION_IMAGE }}:$TAG -t ${{ env.ANONYMIZATION_IMAGE }}:latest -f ${{ env.ANONYMIZATION_DIR }}/Dockerfile ./server
@@ -105,3 +105,49 @@ jobs:
105105
docker build -t ${{ env.GENAI_IMAGE }}:$TAG -t ${{ env.GENAI_IMAGE }}:latest -f ${{ env.GENAI_DIR }}/Dockerfile ${{ env.GENAI_DIR }}
106106
docker push ${{ env.GENAI_IMAGE }}:$TAG
107107
docker push ${{ env.GENAI_IMAGE }}:latest
108+
109+
# deploy-helm:
110+
# name: Deploy to Helm cluster
111+
# needs: build-and-push
112+
# runs-on: ubuntu-latest
113+
114+
# steps:
115+
# - name: Checkout code
116+
# uses: actions/checkout@v4
117+
118+
# - name: Set up kubeconfig
119+
# run: |
120+
# mkdir -p ~/.kube
121+
# echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config
122+
# chmod 600 ~/.kube/config
123+
124+
# - name: Set up kubectl
125+
# uses: azure/setup-kubectl@v3
126+
# with:
127+
# version: v1.30.1
128+
129+
# - name: Install Helm
130+
# run: |
131+
# curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
132+
133+
# - name: Deploy via Helm
134+
# run: |
135+
# helm repo update
136+
137+
# helm upgrade --install oopsops-app \
138+
# ./helm/oopsops \
139+
# --namespace oopsops-test \
140+
# --create-namespace \
141+
# --set client.image.tag=${{ env.TAG }} \
142+
# --set document-service.image.tag=${{ env.TAG }} \
143+
# --set anonymization-service.image.tag=${{ env.TAG }} \
144+
# --set authentication-service.image.tag=${{ env.TAG }} \
145+
# --set genai-service.image.tag=${{ env.TAG }} \
146+
147+
# - name: Verify rollout
148+
# run: |
149+
# kubectl rollout status deployment/authentication-service -n oopsops-test
150+
# kubectl rollout status deployment/document-service -n oopsops-test
151+
# kubectl rollout status deployment/anonymization-service -n oopsops-test
152+
# kubectl rollout status deployment/genai-service -n oopsops-test
153+
# kubectl rollout status deployment/client -n oopsops-test

api/nginx.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ server {
6161
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
6262
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
6363

64-
proxy_pass http://anonymization-service:8094;
64+
proxy_pass http://anonymization-service:8094/api/v1/anonymization/;
6565
client_max_body_size 5m;
6666
proxy_http_version 1.1;
6767
proxy_set_header Host $host;

client/src/components/DocumentArchive.tsx

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
// ABOUTME: Document archive component that displays user's documents from backend API
22
// ABOUTME: Includes filtering, loading states, and proper error handling for document management
3-
import React, { useState, useEffect } from 'react';
3+
import { useState, useEffect } from 'react';
44
import { Link } from 'react-router-dom';
55
import { Button } from '@/components/ui/button';
66
import DocumentStatusFilter from '@/components/DocumentStatusFilter';
77
import { fetchDocuments } from '@/services/documentService';
8+
import { fetchAnonymizations } from '@/services/anonymizeService';
89
import { Document } from '@/types/document';
10+
import { ArchiveDocument } from '@/types/archiveDocument';
11+
912
import { useToast } from '@/hooks/use-toast';
1013

11-
type DocumentStatus = 'All' | 'Anonymized' | 'Original' | 'Summarized';
14+
type DocumentStatus = 'All' | 'Anonymized' | 'Original';
1215

1316
const DocumentArchive = () => {
1417
const [selectedStatus, setSelectedStatus] = useState<DocumentStatus>('All');
15-
const [documents, setDocuments] = useState<Document[]>([]);
18+
const [documents, setDocuments] = useState<ArchiveDocument[]>([]);
1619
const [loading, setLoading] = useState(true);
1720
const [error, setError] = useState<string | null>(null);
1821
const { toast } = useToast();
@@ -22,8 +25,56 @@ const DocumentArchive = () => {
2225
try {
2326
setLoading(true);
2427
setError(null);
25-
const fetchedDocuments = await fetchDocuments();
26-
setDocuments(fetchedDocuments);
28+
29+
// Fetch both original documents and anonymizations in parallel
30+
const [originalDocs, anonymizations] = await Promise.all([
31+
fetchDocuments(),
32+
fetchAnonymizations().catch((err) => {
33+
console.warn('Failed to fetch anonymizations:', err);
34+
// Show a warning toast but don't fail the entire operation
35+
toast({
36+
title: 'Warning',
37+
description:
38+
'Could not load anonymized documents. Showing original documents only.',
39+
variant: 'default',
40+
});
41+
return [];
42+
}),
43+
]);
44+
45+
// Convert original documents to archive format
46+
const archiveOriginals: ArchiveDocument[] = originalDocs.map((doc) => ({
47+
id: doc.id,
48+
fileName: doc.fileName,
49+
uploadDate: doc.uploadDate,
50+
status: 'Original' as const,
51+
documentType: 'original' as const,
52+
documentText: doc.documentText,
53+
}));
54+
55+
// Convert anonymizations to archive format
56+
const archiveAnonymized: ArchiveDocument[] = anonymizations.map(
57+
(anon) => ({
58+
id: anon.id,
59+
fileName: `${getOriginalFileName(
60+
originalDocs,
61+
anon.documentId
62+
)} (Anonymized)`,
63+
uploadDate: anon.created,
64+
status: 'Anonymized' as const,
65+
documentType: 'anonymized' as const,
66+
originalDocumentId: anon.documentId,
67+
documentText: anon.anonymizedText,
68+
})
69+
);
70+
71+
// Combine and sort by upload date (newest first)
72+
const allDocuments = [...archiveOriginals, ...archiveAnonymized].sort(
73+
(a, b) =>
74+
new Date(b.uploadDate).getTime() - new Date(a.uploadDate).getTime()
75+
);
76+
77+
setDocuments(allDocuments);
2778
} catch (err) {
2879
const errorMessage =
2980
err instanceof Error ? err.message : 'Failed to load documents';
@@ -41,6 +92,15 @@ const DocumentArchive = () => {
4192
loadDocuments();
4293
}, [toast]);
4394

95+
// Helper function to get original document filename for anonymized documents
96+
const getOriginalFileName = (
97+
originalDocs: Document[],
98+
documentId: string
99+
): string => {
100+
const originalDoc = originalDocs.find((doc) => doc.id === documentId);
101+
return originalDoc?.fileName || 'Unknown Document';
102+
};
103+
44104
const formatDate = (dateString: string) => {
45105
try {
46106
return new Date(dateString).toLocaleDateString();
@@ -49,6 +109,8 @@ const DocumentArchive = () => {
49109
}
50110
};
51111

112+
// Filter documents based on selected status
113+
52114
const filteredDocuments =
53115
selectedStatus === 'All'
54116
? documents
@@ -112,17 +174,23 @@ const DocumentArchive = () => {
112174
className={`px-2 py-0.5 rounded-full transition-all duration-200 ${
113175
doc.status === 'Anonymized'
114176
? 'bg-green-100 text-green-800 group-hover:bg-green-200'
115-
: doc.status === 'Summarized'
116-
? 'bg-blue-100 text-blue-800 group-hover:bg-blue-200'
117177
: 'bg-gray-100 text-gray-800 group-hover:bg-gray-200'
118178
}`}
119179
>
120180
{doc.status}
121181
</span>
122182
</div>
123-
<Link to={`/editor?id=${doc.id}`}>
183+
<Link
184+
to={`/editor?id=${
185+
doc.documentType === 'anonymized'
186+
? doc.originalDocumentId
187+
: doc.id
188+
}`}
189+
>
124190
<Button className="w-full transition-all duration-200 group-hover:bg-primary/90">
125-
Open Document
191+
{doc.documentType === 'anonymized'
192+
? 'View Anonymized'
193+
: 'Open Document'}
126194
</Button>
127195
</Link>
128196
</div>
@@ -136,7 +204,7 @@ const DocumentArchive = () => {
136204
? "You haven't uploaded any documents yet."
137205
: `No ${selectedStatus.toLowerCase()} documents found.`}
138206
</p>
139-
<Link to="/">
207+
<Link to="/home">
140208
<Button>Upload a document</Button>
141209
</Link>
142210
</div>

client/src/components/DocumentEditor.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
import React, { useState, useEffect } from 'react';
1+
import { useState, useEffect } from 'react';
22
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
33
import { useAnonymization } from '@/hooks/useAnonymization';
44
import { useSummarization } from '@/hooks/useSummarization';
55
import AnonymizationPanel from './AnonymizationPanel';
66
import SummarizationPanel from './SummarizationPanel';
77
import EditAnonymizedDialog from './EditAnonymizedDialog';
88
import { DocumentContent } from '@/types/documentContent';
9-
import { toast } from "sonner";
10-
9+
import { toast } from 'sonner';
1110

1211
type DocumentEditorProps = {
1312
documentId?: string | null;
1413
};
1514

1615
const DocumentEditor = ({ documentId }: DocumentEditorProps) => {
17-
1816
const [activeTab, setActiveTab] = useState('anonymize');
1917
const [documentData, setDocumentData] = useState<DocumentContent>();
2018
const [selectedText, setSelectedText] = useState<string>('');
@@ -28,7 +26,6 @@ const DocumentEditor = ({ documentId }: DocumentEditorProps) => {
2826
const [isAnonymized, setIsAnonymized] = useState(false);
2927
const [isSaved, setIsSaved] = useState(false);
3028

31-
3229
// Custom hooks
3330
const {
3431
anonymizationLevel,
@@ -39,7 +36,14 @@ const DocumentEditor = ({ documentId }: DocumentEditorProps) => {
3936
handleSaveEdit,
4037
handleDownload,
4138
handleSave,
42-
} = useAnonymization(documentData, setDocumentData, isAnonymized, setIsAnonymized, isSaved, setIsSaved);
39+
} = useAnonymization(
40+
documentData,
41+
setDocumentData,
42+
isAnonymized,
43+
setIsAnonymized,
44+
isSaved,
45+
setIsSaved
46+
);
4347

4448
const {
4549
summarizationLevel,
@@ -48,30 +52,28 @@ const DocumentEditor = ({ documentId }: DocumentEditorProps) => {
4852
handleSummarizationLevel,
4953
handleGenerateSummary,
5054
getSummarizationLevelDescription,
51-
handleDownloadSummary
55+
handleDownloadSummary,
5256
} = useSummarization(documentData, setDocumentData);
5357

5458
useEffect(() => {
5559
if (documentData) {
56-
console.log("Current documentData:", documentData);
60+
console.log('Current documentData:', documentData);
5761
}
5862
}, [documentData]);
5963

60-
6164
useEffect(() => {
6265
console.log('Fetching document with ID:', documentId);
6366

6467
const documentInfo = sessionStorage.getItem('currentDocument');
6568
if (documentInfo) {
6669
const parsedInfo = JSON.parse(documentInfo);
6770
const realContent: DocumentContent = {
68-
title: (parsedInfo.fileName?.replace(/\.pdf$/i, "") || "Untitled"),
69-
paragraph: parsedInfo.documentText || "",
71+
title: parsedInfo.fileName?.replace(/\.pdf$/i, '') || 'Untitled',
72+
paragraph: parsedInfo.documentText || '',
7073
sensitive: [],
71-
summary: ""
74+
summary: '',
7275
};
7376

74-
7577
setDocumentData(realContent);
7678
}
7779
}, [documentId]);
@@ -126,7 +128,7 @@ const DocumentEditor = ({ documentId }: DocumentEditorProps) => {
126128
value={activeTab}
127129
onValueChange={(value) => {
128130
if (value === 'summarize' && !isAnonymized) {
129-
toast.warning("Please anonymize your document before summarizing.");
131+
toast.warning('Please anonymize your document before summarizing.');
130132
return;
131133
}
132134
setActiveTab(value);
@@ -136,7 +138,11 @@ const DocumentEditor = ({ documentId }: DocumentEditorProps) => {
136138
<TabsTrigger value="anonymize">Anonymize</TabsTrigger>
137139
<TabsTrigger
138140
value="summarize"
139-
className={!isAnonymized ? 'pointer-events-auto text-muted-foreground opacity-50' : ''}
141+
className={
142+
!isAnonymized
143+
? 'pointer-events-auto text-muted-foreground opacity-50'
144+
: ''
145+
}
140146
>
141147
Summarize
142148
</TabsTrigger>

0 commit comments

Comments
 (0)