diff --git a/frontend/src/pages/SourcesPage.tsx b/frontend/src/pages/SourcesPage.tsx
index e57c277..24a5879 100644
--- a/frontend/src/pages/SourcesPage.tsx
+++ b/frontend/src/pages/SourcesPage.tsx
@@ -279,6 +279,168 @@ const SourcesPage: React.FC = () => {
}
};
+ // Helper function to build example sync URL based on source type and configuration
+ const buildExampleSyncUrl = (): { parts: { text: string; type: 'server' | 'path' | 'folder' | 'file' }[] } | null => {
+ const exampleFile = 'document1.pdf';
+ const firstFolder = formData.watch_folders.length > 0 ? formData.watch_folders[0] : '/Documents';
+
+ if (formData.source_type === 'webdav') {
+ if (!formData.server_url) return null;
+
+ let serverUrl = formData.server_url.trim();
+ // Add https:// if no protocol specified
+ if (!serverUrl.startsWith('http://') && !serverUrl.startsWith('https://')) {
+ serverUrl = `https://${serverUrl}`;
+ }
+ serverUrl = serverUrl.replace(/\/+$/, ''); // Remove trailing slashes
+
+ let webdavPath = '';
+ if (formData.server_type === 'nextcloud') {
+ // Nextcloud uses /remote.php/dav/files/{username}
+ if (!serverUrl.includes('/remote.php/dav/files/')) {
+ webdavPath = `/remote.php/dav/files/${formData.username || 'username'}`;
+ }
+ } else if (formData.server_type === 'owncloud') {
+ // ownCloud uses /remote.php/webdav
+ if (!serverUrl.includes('/remote.php/webdav')) {
+ webdavPath = '/remote.php/webdav';
+ }
+ }
+ // For generic, use the URL as-is
+
+ const cleanFolder = firstFolder.replace(/^\/+/, ''); // Remove leading slashes
+
+ return {
+ parts: [
+ { text: serverUrl, type: 'server' },
+ { text: webdavPath, type: 'path' },
+ { text: `/${cleanFolder}`, type: 'folder' },
+ { text: `/${exampleFile}`, type: 'file' },
+ ],
+ };
+ } else if (formData.source_type === 's3') {
+ if (!formData.bucket_name) return null;
+
+ const endpoint = formData.endpoint_url?.trim() || `https://s3.${formData.region || 'us-east-1'}.amazonaws.com`;
+ const cleanEndpoint = endpoint.replace(/\/+$/, '');
+ const prefix = formData.prefix?.trim().replace(/^\/+|\/+$/g, '') || '';
+ const cleanFolder = firstFolder.replace(/^\/+|\/+$/, '');
+
+ const parts: { text: string; type: 'server' | 'path' | 'folder' | 'file' }[] = [
+ { text: cleanEndpoint, type: 'server' },
+ { text: `/${formData.bucket_name}`, type: 'path' },
+ { text: `/${cleanFolder}`, type: 'folder' },
+ { text: `/${exampleFile}`, type: 'file' },
+ ];
+ // Insert prefix after bucket if present
+ if (prefix) {
+ parts.splice(2, 0, { text: `/${prefix}`, type: 'path' });
+ }
+
+ return { parts };
+ } else if (formData.source_type === 'local_folder') {
+ if (formData.watch_folders.length === 0) return null;
+
+ return {
+ parts: [
+ { text: firstFolder, type: 'folder' },
+ { text: `/${exampleFile}`, type: 'file' },
+ ],
+ };
+ }
+
+ return null;
+ };
+
+ // URL Preview Component
+ const UrlPreviewBox = () => {
+ const urlParts = buildExampleSyncUrl();
+
+ if (!urlParts) return null;
+
+ const getColorForType = (type: 'server' | 'path' | 'folder' | 'file') => {
+ switch (type) {
+ case 'server': return theme.palette.primary.main;
+ case 'path': return theme.palette.info.main;
+ case 'folder': return theme.palette.success.main;
+ case 'file': return theme.palette.text.secondary;
+ default: return theme.palette.text.primary;
+ }
+ };
+
+ const getLabelForType = (type: 'server' | 'path' | 'folder' | 'file') => {
+ switch (type) {
+ case 'server': return 'Server URL';
+ case 'path': return formData.source_type === 'webdav' ? 'WebDAV Path' : 'Bucket/Prefix';
+ case 'folder': return 'Watch Directory';
+ case 'file': return 'Example File';
+ default: return '';
+ }
+ };
+
+ // Get unique types for legend
+ const uniqueTypes = Array.from(new Set(urlParts.parts.map(p => p.type)));
+
+ return (
+
+
+ Example sync URL:
+
+
+ {urlParts.parts.map((part, index) => (
+
+ {part.text}
+
+ ))}
+
+
+ {uniqueTypes.map((type) => (
+
+
+
+ {getLabelForType(type)}
+
+
+ ))}
+
+
+ );
+ };
+
const handleCreateSource = () => {
setEditingSource(null);
setFormData({
@@ -1635,7 +1797,7 @@ const SourcesPage: React.FC = () => {
Folders to Monitor
-
+
Specify which folders to scan for files. Use absolute paths starting with "/".
@@ -1646,14 +1808,14 @@ const SourcesPage: React.FC = () => {
value={newFolder}
onChange={(e) => setNewFolder(e.target.value)}
placeholder="/Documents"
- sx={{
+ sx={{
flexGrow: 1,
- '& .MuiOutlinedInput-root': { borderRadius: 2 }
+ '& .MuiOutlinedInput-root': { borderRadius: 2 }
}}
/>
-