Add authentication #17
4 changed files with 89 additions and 80 deletions
|
@ -19,11 +19,15 @@ export const FileViewerModalWrapper: React.FC = () => {
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
|
|
||||||
// Listen for custom events to open/close modal and set files
|
// Listen for custom events to open/close modal and set files
|
||||||
const handleShowFiles = (event: CustomEvent) => {
|
const handleShowFiles = (event: CustomEvent) => {
|
||||||
|
if (mounted) {
|
||||||
const { files } = event.detail;
|
const { files } = event.detail;
|
||||||
setFiles(Array.isArray(files) ? files : [files]);
|
setFiles(Array.isArray(files) ? files : [files]);
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listeners
|
// Add event listeners
|
||||||
|
@ -31,14 +35,38 @@ export const FileViewerModalWrapper: React.FC = () => {
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
return () => {
|
return () => {
|
||||||
|
mounted = false;
|
||||||
window.removeEventListener('showFileViewer' as any, handleShowFiles);
|
window.removeEventListener('showFileViewer' as any, handleShowFiles);
|
||||||
|
// Reset state on unmount
|
||||||
|
setIsOpen(false);
|
||||||
|
setFiles([]);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Handle tab visibility changes
|
||||||
|
useEffect(() => {
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
setIsOpen(false);
|
||||||
|
setFiles([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
|
setFiles([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only render the modal if we have files and it should be open
|
||||||
|
if (!isOpen || files.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileViewerModal
|
<FileViewerModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
|
@ -57,6 +85,16 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
|
|
||||||
const fileArray = Array.isArray(files) ? files : [files];
|
const fileArray = Array.isArray(files) ? files : [files];
|
||||||
|
|
||||||
|
// Reset state when modal closes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
setSelectedFile(null);
|
||||||
|
setShowPreview(false);
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
// Helper function to check if file type is previewable
|
// Helper function to check if file type is previewable
|
||||||
const isPreviewableType = (fileType: string): boolean => {
|
const isPreviewableType = (fileType: string): boolean => {
|
||||||
return (
|
return (
|
||||||
|
@ -71,21 +109,19 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
// Only set loading if the file is previewable
|
// Only show file directly if there's exactly one file
|
||||||
if (selectedFile && isPreviewableType(selectedFile.type)) {
|
if (fileArray.length === 1) {
|
||||||
setLoading(true);
|
const fileToShow = fileArray[0];
|
||||||
|
setSelectedFile(fileToShow);
|
||||||
|
setShowPreview(true);
|
||||||
|
setLoading(isPreviewableType(fileToShow.type));
|
||||||
} else {
|
} else {
|
||||||
|
// For multiple files, show the file browser
|
||||||
|
setShowPreview(false);
|
||||||
|
setSelectedFile(null);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
setError(null);
|
setError(null);
|
||||||
// If single file, show preview directly
|
|
||||||
if (!Array.isArray(files)) {
|
|
||||||
setSelectedFile(files);
|
|
||||||
setShowPreview(true);
|
|
||||||
} else {
|
|
||||||
setShowPreview(false);
|
|
||||||
setSelectedFile(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [isOpen, files]);
|
}, [isOpen, files]);
|
||||||
|
|
||||||
|
@ -101,7 +137,6 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
const handleFileSelect = (file: FileType) => {
|
const handleFileSelect = (file: FileType) => {
|
||||||
setSelectedFile(file);
|
setSelectedFile(file);
|
||||||
setShowPreview(true);
|
setShowPreview(true);
|
||||||
// Only set loading if the file is previewable
|
|
||||||
setLoading(isPreviewableType(file.type));
|
setLoading(isPreviewableType(file.type));
|
||||||
setError(null);
|
setError(null);
|
||||||
};
|
};
|
||||||
|
@ -198,7 +233,6 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for unsupported file types
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center p-8">
|
<div className="flex flex-col items-center justify-center p-8">
|
||||||
<div className="text-4xl mb-4">📄</div>
|
<div className="text-4xl mb-4">📄</div>
|
||||||
|
@ -253,19 +287,6 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clone the modal with a new ID
|
|
||||||
const cloneModal = () => {
|
|
||||||
const newModalId = `${modalId}-${Date.now()}`;
|
|
||||||
return (
|
|
||||||
<FileViewerModal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={onClose}
|
|
||||||
files={files}
|
|
||||||
modalId={newModalId}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -283,13 +304,14 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
{showPreview && selectedFile ? (
|
{showPreview && selectedFile ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
{fileArray.length > 1 && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-ghost btn-sm"
|
className="btn btn-ghost btn-sm"
|
||||||
onClick={handleBackToList}
|
onClick={handleBackToList}
|
||||||
style={{ display: Array.isArray(files) ? 'block' : 'none' }}
|
|
||||||
>
|
>
|
||||||
← Back
|
← Back
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
<h3 className="font-bold text-lg truncate">{selectedFile.name}</h3>
|
<h3 className="font-bold text-lg truncate">{selectedFile.name}</h3>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -297,25 +319,6 @@ const FileViewerModal: React.FC<FileViewerModalProps> = ({ isOpen, onClose, file
|
||||||
<h3 className="font-bold text-lg">File Browser</h3>
|
<h3 className="font-bold text-lg">File Browser</h3>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
|
||||||
className="btn btn-circle btn-ghost"
|
|
||||||
onClick={cloneModal}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
className="h-6 w-6"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle btn-ghost"
|
className="btn btn-circle btn-ghost"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
---
|
---
|
||||||
// Import the FileViewerModal component
|
|
||||||
import FileViewerModal from "../modals/FileViewerModal";
|
|
||||||
|
|
||||||
// Define the component's props and setup
|
// Define the component's props and setup
|
||||||
interface Props {}
|
interface Props {}
|
||||||
const {} = Astro.props;
|
const {} = Astro.props;
|
||||||
|
@ -124,9 +121,6 @@ const {} = Astro.props;
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add the FileViewerModal component -->
|
|
||||||
<FileViewerModal client:load />
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// TypeScript declarations
|
// TypeScript declarations
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
// Import the majors list and FilePreviewModal
|
// Import the majors list
|
||||||
import allMajors from "../../data/allUCSDMajors.txt?raw";
|
import allMajors from "../../data/allUCSDMajors.txt?raw";
|
||||||
import FilePreviewModal from "../modals/FilePreviewModal.astro";
|
|
||||||
|
|
||||||
const majorsList: string[] = allMajors
|
const majorsList: string[] = allMajors
|
||||||
.split("\n")
|
.split("\n")
|
||||||
|
@ -183,7 +182,7 @@ const majorsList: string[] = allMajors
|
||||||
<button
|
<button
|
||||||
id="previewResume"
|
id="previewResume"
|
||||||
class="btn btn-sm btn-ghost"
|
class="btn btn-sm btn-ghost"
|
||||||
onclick="resumeViewer.showModal()"
|
onclick="showResumePreview(resumeUrl, fileName)"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -228,24 +227,19 @@ const majorsList: string[] = allMajors
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Replace the old resume viewer modal with the new FilePreviewModal -->
|
|
||||||
<FilePreviewModal id="resumeViewer" title="Resume Preview" />
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// TypeScript declarations
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
showEventFiles: (eventId: string) => Promise<void>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import { Authentication } from "../pocketbase/Authentication";
|
import { Authentication } from "../pocketbase/Authentication";
|
||||||
import { Update } from "../pocketbase/Update";
|
import { Update } from "../pocketbase/Update";
|
||||||
import { SendLog } from "../pocketbase/SendLog";
|
import { SendLog } from "../pocketbase/SendLog";
|
||||||
import { FileManager } from "../pocketbase/FileManager";
|
import { FileManager } from "../pocketbase/FileManager";
|
||||||
|
|
||||||
// Add type declaration for the global filePreviewModal
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
filePreviewModal: {
|
|
||||||
show: (file: { url: string; name: string }) => void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auth = Authentication.getInstance();
|
const auth = Authentication.getInstance();
|
||||||
const update = Update.getInstance();
|
const update = Update.getInstance();
|
||||||
const logger = SendLog.getInstance();
|
const logger = SendLog.getInstance();
|
||||||
|
@ -272,6 +266,23 @@ const majorsList: string[] = allMajors
|
||||||
const resumeDisplay = document.getElementById("resumeDisplay");
|
const resumeDisplay = document.getElementById("resumeDisplay");
|
||||||
const previewResume = document.getElementById("previewResume");
|
const previewResume = document.getElementById("previewResume");
|
||||||
|
|
||||||
|
// Function to show resume in FileViewerModal
|
||||||
|
const showResumePreview = (url: string, fileName: string) => {
|
||||||
|
// Create and dispatch custom event for FileViewerModal
|
||||||
|
const showFileViewerEvent = new CustomEvent("showFileViewer", {
|
||||||
|
detail: {
|
||||||
|
files: {
|
||||||
|
url,
|
||||||
|
name: fileName,
|
||||||
|
type: fileName.toLowerCase().endsWith(".pdf")
|
||||||
|
? "application/pdf"
|
||||||
|
: "application/octet-stream",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
window.dispatchEvent(showFileViewerEvent);
|
||||||
|
};
|
||||||
|
|
||||||
// Load current user data
|
// Load current user data
|
||||||
const loadUserData = () => {
|
const loadUserData = () => {
|
||||||
if (auth.isAuthenticated()) {
|
if (auth.isAuthenticated()) {
|
||||||
|
@ -300,10 +311,7 @@ const majorsList: string[] = allMajors
|
||||||
// Update preview button to use new modal
|
// Update preview button to use new modal
|
||||||
if (previewResume) {
|
if (previewResume) {
|
||||||
previewResume.onclick = () => {
|
previewResume.onclick = () => {
|
||||||
window.filePreviewModal.show({
|
showResumePreview(resumeUrl, fileName);
|
||||||
url: resumeUrl,
|
|
||||||
name: fileName,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import UserProfile from "../components/auth/UserProfile.astro";
|
||||||
import DefaultProfileView from "../components/profile/DefaultProfileView.astro";
|
import DefaultProfileView from "../components/profile/DefaultProfileView.astro";
|
||||||
import OfficerProfileView from "../components/profile/OfficerView.astro";
|
import OfficerProfileView from "../components/profile/OfficerView.astro";
|
||||||
import UserSettings from "../components/profile/UserSettings.astro";
|
import UserSettings from "../components/profile/UserSettings.astro";
|
||||||
|
import FileViewerModal from "../components/modals/FileViewerModal";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import profileConfig from "../config/profileConfig.yaml?raw";
|
import profileConfig from "../config/profileConfig.yaml?raw";
|
||||||
import textConfig from "../config/text.yml?raw";
|
import textConfig from "../config/text.yml?raw";
|
||||||
|
@ -213,6 +214,9 @@ const text = yaml.load(textConfig) as any;
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add FileViewerModal here, outside of the tab content -->
|
||||||
|
<FileViewerModal client:load />
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue