{"version":3,"sources":["atoms.tsx","clients/apiClient.tsx","clients/apiHooks.ts","components/collection/AlbumList.tsx","components/generic/ResourceInput.tsx","components/generic/ErrorBoundary.tsx","components/collection/CollectionView.tsx","components/collection/NewCollectionView.tsx","components/generic/index.tsx","components/tags/Items.tsx","components/tags/TagsLabels.tsx","components/album/AssetTile.tsx","components/album/DocumentView.tsx","components/album/AssetDetails.tsx","components/album/UploadList.tsx","components/album/Uploads.tsx","components/album/AlbumView.tsx","components/album/NewAlbumView.tsx","components/navigation/CollectionListLink.tsx","components/navigation/CollectionList.tsx","components/navigation/AlbumListLink.tsx","components/navigation/AlbumList.tsx","components/navigation/TopLevelNavigation.tsx","components/LandingView.tsx","components/LoginView.tsx","App.tsx","reportWebVitals.ts","index.tsx"],"names":["UploadStatus","activeUploads","atom","key","default","selectedAssetDictionary","apiUrl","path","prefix","apiRoot","normPath","startsWith","apiFetch","config","a","token","localStorage","getItem","fetch","headers","then","response","status","json","Error","uploadFile","fileUpload","state","NOT_STARTED","formData","FormData","append","file","collectionId","albumId","method","body","getWorkspace","map","d","name","id","getAlbums","albums","createAlbum","newPayload","Accept","JSON","stringify","fetchAssets","assetResponse","assets","asset","url","link","tags","t","value","updateAssetMetadata","newValues","assetId","newTags","labels","deleteAsset","patchAlbum","useWorkspace","loadable","useRecoilValueLoadable","workspaceAtom","contents","useCollection","collectionSelector","CollectionContext","createContext","AlbumContext","useSelectedAssets","useRecoilState","selectedAssets","setSelectedAssets","getSelectedAssets","Object","keys","filter","isSelected","getAssetState","toggleSelectAsset","currentState","prevState","useUploads","atomFiles","setAtomFiles","refreshAssets","useAssetsHook","handleUpload","forEach","currentFiles","fileIndex","indexOf","slice","SUCCESS","catch","error","console","FAILURE","finally","handleFileSelect","evt","newFiles","target","files","newFileUpload","push","concat","removeFile","filename","useAlbumsHook","albumsAtom","setAlbums","_refreshAlbums","refreshAlbums","selector","get","atomFamily","selectorFamily","assetsAtom","params","setAssets","albumWithAssetsSelector","album","assetSelector","charAt","toUpperCase","AlbumList","to","sort","b","localeCompare","ResourceInput","props","useState","initialValue","setValue","onSubmit","onChange","submitOnBlur","submitDisabled","handleSubmit","useCallback","preventDefault","handleChange","handleBlur","className","type","inputClass","autoFocus","placeholder","onBlur","cta","disabled","ErrorBoundary","errorInfo","this","setState","children","React","Component","CollectionView","useParams","collection","isEditName","setEditName","val","onClick","CollectionExistsErr","NewCollectionView","navigate","useNavigate","duplicationErr","setDuplicationErr","createLoading","setCreateLoading","collections","c","trim","length","createCollection","replace","err","Backdrop","tw","div","ContentWrapper","styled","centered","Modal","handleKeyPress","event","onClose","useEffect","document","addEventListener","removeEventListener","Icon","LabelItem","isHighlight","onDelete","TagItem","tag","newValuesFn","setKey","showValue","setShowValue","inputRef","useRef","current","focus","onKeyUp","keyCode","newKey","includes","ref","newValue","saveNewAttribute","ItemGroupContainer","ul","editMode","ItemGroup","onSelect","setSelectState","handleSelect","index","item","idx","l","Labels","onCreate","setEditMode","handleClick","handleInput","handleEnterEditMode","stopPropagation","undefined","EditLabel","label","initialState","mode","isDirty","editTagIdx","editLabelIdx","actionHandler","action","newState","TagsLabels","useReducer","dispatch","enableEdit","disableEdit","selectTag","selectLabel","updateLabel","isSelectedTag","isSelectedLabel","labelToEdit","tagToEdit","useComponentState","useAlbumHook","useContext","style","content","e","new_vals","AssetTile","assetState","match","src","alt","href","memo","DocumentView","numPages","setNumPages","pageNumber","setPageNumber","changePage","offset","prevPageNumber","fullUrl","onLoadSuccess","onItemClick","ActionButton","css","AssetDetails","pdfUrl","ctxAlbumId","ctxCollectionId","useRecoilValue","metadata","useAsset","currIdx","findIndex","ast","prevIdx","nextIdx","prev","next","title","album_labels","dataPayload","album_tags","download_path","download","UploadList","displayFiles","f","size","Uploads","hidden","multiple","AlbumView","showUpload","handleCloseAssetDetails","Provider","NewAlbumView","CollectionListLink","routeMatch","useMatch","CollectionList","loading","AlbumListLink","TopLevelNavigation","element","fallback","LandingPage","LoginView","setItem","PrivateRoute","App","LandingView","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","getElementById"],"mappings":"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","file":"static/js/main.2a6e1d7f.chunk.js","sourcesContent":["import { atom } from \"recoil\";\n\nexport enum UploadStatus {\n NOT_STARTED = \"NOT_STARTED\",\n SUCCESS = \"SUCCESS\",\n FAILURE = \"FAILURE\",\n IN_PROGRESS = \"IN_PROGRESS\",\n}\n\nexport interface FileUpload {\n file: File;\n albumId: string;\n collectionId: string;\n state: UploadStatus;\n}\n\nexport const activeUploads = atom({\n key: \"UploadList\",\n default: [] as FileUpload[],\n});\n\nexport const selectedAssetDictionary = atom({\n key: \"SelectedAssets\",\n default: {} as { [key: string]: { isSelected: boolean } },\n});\n","import { FileUpload, UploadStatus } from \"../atoms\";\nimport { AssetAugmentation } from \"./apiHooks\";\n\ndeclare var __API_ROOT__: string;\n\nexport function apiUrl(path: string, prefix: string = \"/api\") {\n const apiRoot = (typeof __API_ROOT__ !== \"undefined\" && __API_ROOT__) || \"\";\n const normPath = `${path.startsWith(\"/\") ? \"\" : \"/\"}${path}`;\n return `${apiRoot}${prefix}${normPath}`;\n}\n\nasync function apiFetch(path: string, config?: RequestInit) {\n const token = localStorage.getItem(\"token\");\n if (!token) {\n //TODO: something more intelligent\n return;\n }\n return fetch(apiUrl(path), {\n ...config,\n headers: { token, ...config?.headers },\n }).then((response) => {\n if (response.status >= 200 && response.status < 300) {\n return response.json();\n } else {\n throw new Error(`Something went wrong when calling ${path}`);\n }\n })\n}\n\nexport async function uploadFile(fileUpload: FileUpload) {\n if (fileUpload.state !== UploadStatus.NOT_STARTED) {\n return; //we already tried to upload this before, skip.\n }\n const formData = new FormData();\n formData.append(\"upload\", fileUpload.file);\n\n return apiFetch(\n `/collections/${fileUpload.collectionId}/albums/${fileUpload.albumId}/assets`,\n {\n method: \"POST\",\n body: formData,\n }\n );\n}\n\nexport interface CollectionBase {\n name: string;\n id: string;\n}\n\nexport type Workspace = CollectionBase[];\n\nexport async function getWorkspace(): Promise {\n return (await apiFetch(`/collections`)).map((d: string) => ({\n name: d,\n id: d,\n }));\n}\n\nexport interface Album {\n name: string;\n id: string;\n collectionId: string;\n labels: Label[];\n tags: Tag[];\n}\n\nexport interface Collection {\n name: string;\n id: string;\n albums: Album[];\n}\n\nexport async function getAlbums(collectionId: string): Promise {\n const albums = (await apiFetch(`/collections/${collectionId}`)).map(\n (a: { name: string; id: string }) => ({\n name: a.name,\n id: a.id,\n collectionId,\n })\n );\n\n return albums;\n}\n\nexport async function createAlbum(\n collectionId: string,\n name: string\n): Promise {\n const newPayload = { name };\n return apiFetch(`/collections/${collectionId}/albums`, {\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n body: JSON.stringify(newPayload),\n });\n}\n\nexport interface Asset {\n id: string;\n name: string;\n collectionId: string;\n albumId: string;\n path: string;\n download_path: string;\n url: string;\n labels: Label[];\n tags: Tag[];\n album_labels?: Label[];\n album_tags?: Tag[];\n}\n\nexport interface Tag {\n key: string;\n value: string | number;\n}\n\nexport type Label = string;\n\nexport interface AlbumWithAssets {\n id: string;\n name: string;\n collectionId: string;\n assets: Asset[];\n labels: Label[];\n tags: Tag[];\n}\n\nexport async function fetchAssets(\n collectionId: string,\n albumId: string\n): Promise<(Asset & AssetAugmentation)[]> {\n const assetResponse = await apiFetch(\n `/collections/${collectionId}/assets?album=${albumId}`\n );\n\n return assetResponse.assets.map((asset: any) => ({\n ...asset,\n collectionId,\n albumId,\n url: apiUrl(asset.path, \"\"),\n link: `/collection/${collectionId}/a/${albumId}/s/${asset.id}`,\n tags: asset.tags.map((t: [string, string]) => ({\n key: t[0],\n value: t[1],\n })),\n }));\n}\n\nexport async function updateAssetMetadata(\n newValues: { labels?: Label[]; tags?: Tag[] },\n collectionId: string,\n albumId: string,\n assetId: string\n) {\n const newTags = newValues.tags?.map(t => [t.key, t.value]);\n\n const newPayload = { labels: newValues.labels, tags: newTags };\n return apiFetch(\n `/collections/${collectionId}/albums/${albumId}/assets/${assetId}/metadata`,\n {\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n body: JSON.stringify(newPayload),\n }\n );\n}\n\nexport async function deleteAsset(\n collectionId: string,\n albumId: string,\n assetId: string\n): Promise {\n await apiFetch(\n `/collections/${collectionId}/albums/${albumId}/assets/${assetId}`,\n { method: \"DELETE\" }\n );\n\n return null;\n}\n\nexport async function createCollection(name: string): Promise {\n return {\n id: name,\n name,\n };\n}\n\nexport async function patchAlbum(\n collectionId: string,\n albumId: string,\n newValues: { labels: Label[]; tags: Tag[] }\n) {\n let newTags = newValues.tags.map(({ key, value }) => [key, value]);\n const newPayload = { labels: newValues.labels, tags: newTags };\n return apiFetch(`/collections/${collectionId}/albums/${albumId}`, {\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n method: \"PATCH\",\n body: JSON.stringify(newPayload),\n });\n}\n","import {\n createContext,\n useContext,\n useCallback,\n} from \"react\";\n\nimport {\n AlbumWithAssets,\n Asset,\n Collection,\n Workspace,\n uploadFile,\n Album,\n getAlbums,\n getWorkspace,\n createAlbum,\n fetchAssets,\n deleteAsset,\n Label,\n Tag,\n patchAlbum,\n updateAssetMetadata,\n} from \"./apiClient\";\n\nimport { activeUploads, selectedAssetDictionary, UploadStatus } from \"../atoms\";\nimport { atom, atomFamily, selector, selectorFamily, useRecoilState, useRecoilValue, useRecoilValueLoadable } from \"recoil\";\n\nexport function useWorkspace(): [Workspace | null, boolean, boolean] {\n // TODO: \"The\" react way would be Suspense and ErrorBoundary..\n const loadable = useRecoilValueLoadable(workspaceAtom);\n switch (loadable.state) {\n case 'hasValue':\n return [loadable.contents, false, false];\n case 'loading':\n return [null, true, false];\n case 'hasError':\n return [null, false, true];\n }\n}\n\n\nexport function useCollection(\n collectionId: string | undefined | null\n): [Collection | null, boolean, boolean] {\n // TODO: \"The\" react way would be Suspense and ErrorBoundary..\n const loadable = useRecoilValueLoadable(collectionSelector(collectionId))\n switch (loadable.state) {\n case 'hasValue':\n return [loadable.contents, false, false];\n case 'loading':\n return [null, true, false];\n case 'hasError':\n return [null, false, true];\n }\n}\n\nexport const CollectionContext = createContext(null);\nexport const AlbumContext = createContext(null);\nexport interface AssetAugmentation {\n link: string;\n}\n\nexport interface AssetState {\n isSelected?: boolean;\n}\n\nexport function useSelectedAssets() {\n const [selectedAssets, setSelectedAssets] = useRecoilState(\n selectedAssetDictionary\n );\n\n function toggleSelectAsset(assetId: string, currentState: boolean) {\n setSelectedAssets((prevState) => ({\n ...prevState,\n [assetId]: { isSelected: !currentState },\n }));\n }\n\n function getSelectedAssets() {\n return Object.keys(selectedAssets).filter(\n (id) => selectedAssets[id]?.isSelected\n );\n }\n function getAssetState(assetId: string) {\n if (selectedAssets[assetId]) {\n return selectedAssets[assetId];\n }\n return { isSelected: false };\n }\n return {\n selectedAssets,\n getSelectedAssets,\n getAssetState,\n toggleSelectAsset,\n };\n}\n\nexport function useUploads(albumId: string, collectionId: string) {\n const [atomFiles, setAtomFiles] = useRecoilState(activeUploads);\n const { refreshAssets } = useAssetsHook({ albumId, collectionId });\n\n\n function handleFileSelect(evt: any) {\n const newFiles = [];\n for (const f of evt.target.files) {\n const newFileUpload = {\n file: f,\n albumId: albumId,\n collectionId: collectionId,\n state: UploadStatus.NOT_STARTED,\n };\n newFiles.push(newFileUpload);\n }\n setAtomFiles(atomFiles.concat(newFiles));\n }\n\n function removeFile(filename: string) {\n setAtomFiles(\n atomFiles.filter((fileUpload) => {\n return fileUpload.file.name !== filename;\n })\n );\n }\n\n const handleUpload = async (albumId: string, collectionId: string) => {\n atomFiles\n .filter(\n (fileUpload) =>\n fileUpload.albumId === albumId &&\n fileUpload.collectionId === collectionId\n )\n .forEach((fileUpload) => {\n uploadFile(fileUpload)\n .then((response) => {\n setAtomFiles((currentFiles) => {\n const fileIndex = currentFiles.indexOf(fileUpload);\n const newAtomFiles = [\n ...currentFiles.slice(0, fileIndex),\n { ...fileUpload, state: UploadStatus.SUCCESS },\n ...currentFiles.slice(fileIndex + 1),\n ];\n return newAtomFiles;\n });\n })\n .catch((error) => {\n console.error(error);\n setAtomFiles((currentFiles) => {\n const fileIndex = currentFiles.indexOf(fileUpload);\n const newAtomFiles = [\n ...currentFiles.slice(0, fileIndex),\n { ...fileUpload, state: UploadStatus.FAILURE },\n ...currentFiles.slice(fileIndex + 1),\n ];\n return newAtomFiles;\n });\n })\n .finally(async () => {\n refreshAssets();\n });\n });\n };\n\n return { atomFiles, handleFileSelect, handleUpload, removeFile };\n}\n\nexport function useAlbumsHook(collectionId: string) {\n const [, setAlbums] = useRecoilState(albumsAtom(collectionId))\n\n const _refreshAlbums = async () => {\n const albums = await getAlbums(collectionId);\n\n setAlbums(albums);\n }\n\n const _createAlbum = async (name: string) => {\n await createAlbum(collectionId, name);\n _refreshAlbums();\n }\n\n return { createAlbum: _createAlbum, refreshAlbums: _refreshAlbums }\n}\n\nexport function useAlbumHook(collectionId: string, albumId: string) {\n const { refreshAlbums } = useAlbumsHook(collectionId);\n const { refreshAssets } = useAssetsHook({ collectionId, albumId });\n\n const _patchAlbum = async (newValues: { labels: Label[]; tags: Tag[] }) => {\n patchAlbum(collectionId, albumId, newValues)\n .then(() => {\n refreshAlbums();\n refreshAssets();\n })\n .catch((err) => {\n console.error(err);\n refreshAlbums();\n refreshAssets();\n })\n }\n\n return { patchAlbum: _patchAlbum }\n}\n\n/*\n These atoms (workspace, albums and assets) correspond to our backend entities and should follow those definitions\n closely.\n Everything else we need (for convenience in the UI) should be derived selectors from those.\n*/\n\nexport const workspaceAtom = atom({\n key: 'Workspace',\n default: selector({\n key: 'Workspace/Default',\n get: async () => {\n return await getWorkspace();\n },\n }),\n});\n\n\nexport const albumsAtom = atomFamily({\n key: 'AlbumsByCollection',\n default: selectorFamily({\n key: 'AlbumsByCollection/Default',\n get: collectionId => () => {\n return getAlbums(collectionId);\n },\n }),\n});\n\n\nexport const assetsAtom = atomFamily({\n key: 'AssetsByAlbum',\n default: selectorFamily({\n key: 'AssetsByAlbum/Default',\n get: ({ collectionId, albumId }) => () => {\n return fetchAssets(collectionId, albumId);\n },\n }),\n});\n\n\nexport function useAssetsHook(params: { collectionId: string, albumId: string }) {\n const [, setAssets] = useRecoilState(assetsAtom(params))\n\n const _deleteAsset = async (assetId: string) => {\n await deleteAsset(params.collectionId, params.albumId, assetId);\n const albums = await fetchAssets(params.collectionId, params.albumId);\n\n setAssets(albums)\n }\n\n const _refreshAssets = async () => {\n const albums = await fetchAssets(params.collectionId, params.albumId);\n setAssets(albums)\n }\n\n return { deleteAsset: _deleteAsset, refreshAssets: _refreshAssets }\n}\n\nexport function useAsset(params: { assetId: string, albumId?: string, collectionId?: string }) {\n const ctxAlbumId = useContext(AlbumContext);\n const ctxCollectionId = useContext(CollectionContext);\n\n const assetId = params.assetId;\n const collectionId = params.collectionId || ctxCollectionId;\n const albumId = params.albumId || ctxAlbumId;\n\n const asset = useRecoilValue(\n assetSelector({\n collectionId: collectionId || '',\n albumId: albumId || '',\n assetId: params.assetId,\n })\n );\n\n const [, setAssets] = useRecoilState(assetsAtom({\n collectionId: collectionId || '',\n albumId: albumId || ''\n }))\n\n const _deleteAsset = useCallback(async () => {\n if(!collectionId || !albumId) {\n return\n }\n await deleteAsset(collectionId, albumId, assetId);\n const albums = await fetchAssets(collectionId, albumId);\n setAssets(albums)\n }, [collectionId, albumId, assetId, setAssets]);\n\n const _updateAssetMetadata = useCallback(async (metadata) => {\n if(!collectionId || !albumId) {\n return\n }\n await updateAssetMetadata(metadata, collectionId, albumId, assetId);\n const albums = await fetchAssets(collectionId, albumId);\n setAssets(albums)\n }, [collectionId, albumId, assetId, setAssets])\n\n return {\n asset,\n deleteAsset: _deleteAsset,\n updateAssetMetadata: _updateAssetMetadata\n }\n}\n\n\nexport const albumWithAssetsSelector = selectorFamily({\n key: 'AlbumWithAssets',\n get: ({ collectionId, albumId }) => ({ get }) => {\n const albums = get(albumsAtom(collectionId));\n const assets = get(assetsAtom({ collectionId, albumId }));\n let album = albums.filter(a => a.id === albumId)[0];\n\n return {\n ...album,\n assets: assets,\n }\n },\n});\n\n\nexport const assetSelector = selectorFamily({\n key: 'SingleAssetSelector',\n get: ({ collectionId, albumId, assetId }) => ({ get }) => {\n const assets = get(assetsAtom({ collectionId, albumId }));\n return assets.filter(a => a.id === assetId)[0];\n },\n});\n\n\nexport const collectionSelector = selectorFamily({\n key: 'Collection',\n get: (collectionId) => async ({ get }) => {\n if (!collectionId) {\n return null;\n }\n const albums = get(albumsAtom(collectionId))\n\n return {\n name: collectionId.charAt(0).toUpperCase() + collectionId.slice(1),\n id: collectionId,\n albums,\n }\n },\n});\n","import \"twin.macro\";\n\nimport { Link } from \"react-router-dom\";\n\nimport { Album } from \"../../clients/apiClient\";\n\nexport interface AlbumListProps {\n collectionId: string;\n albums: Album[];\n filter?: string;\n}\n\n// const Foo = tw.div`btn text-5xl font-bold`; //\n\nfunction AlbumList({ albums, collectionId }: AlbumListProps): JSX.Element {\n return (\n
\n Albums\n \n \n \n
\n filter / sort / group by ...\n
\n {[...albums]\n .sort((a, b) => a.id.localeCompare(b.id))\n .map((a) => (\n \n

{a.name} >>

\n \n ))}\n
\n );\n}\n\nexport default AlbumList;\n","import \"twin.macro\";\n\nimport { useState, useCallback } from \"react\";\n\nexport interface CreateResourceInputProps {\n onSubmit: (val: string) => void;\n onChange?: (val: string) => void;\n submitOnBlur?: boolean;\n submitDisabled?: boolean;\n\n placeholder?: string;\n initialValue?: string;\n\n error?: string | JSX.Element | JSX.Element[] | null;\n cta?: string;\n className?: string;\n inputClass?: string;\n}\n\nfunction ResourceInput(props: CreateResourceInputProps): JSX.Element {\n const [value, setValue] = useState(props.initialValue || \"\");\n\n const { onSubmit, onChange, submitOnBlur, submitDisabled } = props;\n\n const handleSubmit = useCallback(\n (evt: React.FormEvent) => {\n evt.preventDefault();\n if (!submitDisabled) {\n onSubmit(value);\n }\n },\n [onSubmit, submitDisabled, value]\n );\n\n const handleChange = useCallback(\n (evt: React.ChangeEvent) => {\n setValue(evt.target.value);\n if (onChange) {\n onChange(evt.target.value);\n }\n },\n [onChange]\n );\n\n const handleBlur = useCallback(\n (evt: React.FormEvent) => {\n if (submitOnBlur && !submitDisabled) {\n onSubmit(value);\n }\n },\n [submitOnBlur, onSubmit, submitDisabled, value]\n );\n\n return (\n
\n \n {props.error ? (\n
{props.error}
\n ) : null}\n \n \n );\n}\n\nexport default ResourceInput;\n","import React from \"react\";\n\nexport default class ErrorBoundary extends React.Component<{}, { error: Error | null }> {\n constructor(props: {}) {\n super(props)\n this.state = { error: null }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(errorInfo)\n this.setState({ error })\n }\n\n render() {\n if(this.state.error) {\n return
this.state.error
\n } else {\n return this.props.children\n }\n }\n}\n","import \"twin.macro\";\n\nimport { useState, useCallback } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { useCollection } from \"../../clients/apiHooks\";\n\nimport AlbumList from \"./AlbumList\";\nimport ResourceInput from \"../generic/ResourceInput\";\nimport ErrorBoundary from \"../generic/ErrorBoundary\";\n\nfunction CollectionView(): JSX.Element | null {\n const { id } = useParams<{ id?: string }>();\n const [collection /*, loading, err*/] = useCollection(id);\n const [isEditName, setEditName] = useState(false);\n\n const handleSubmit = useCallback((val: string) => {\n setEditName(false);\n }, []);\n\n if (!id) {\n return null;\n }\n\n return (\n \n
\n {collection && (\n
\n Collection\n {isEditName ? (\n \n ) : (\n

setEditName(true)}>\n {collection.name}\n

\n )}\n \n
\n )}\n
\n
\n );\n}\n\nexport default CollectionView;\n","import \"twin.macro\";\n\nimport { useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useWorkspace } from \"../../clients/apiHooks\";\nimport { createCollection } from \"../../clients/apiClient\";\n\nimport ResourceInput from \"../generic/ResourceInput\";\n\nfunction CollectionExistsErr(): JSX.Element {\n return (\n
\n A collection with that name already exists in your\n workspace.\n
\n );\n}\n\nfunction NewCollectionView(): JSX.Element {\n const navigate = useNavigate();\n const [duplicationErr, setDuplicationErr] = useState(false);\n const [createLoading, setCreateLoading] = useState(false);\n const [collections /*, loading, err*/] = useWorkspace();\n\n function changeHandler(val: string) {\n setDuplicationErr(\n !!collections && !!collections.filter((c) => c.name === val.trim()).length\n );\n }\n\n function submitHandler(val: string) {\n setCreateLoading(true);\n createCollection(val.trim())\n .then((c) => {\n setCreateLoading(false);\n navigate(`/collection/${c.id}`, { replace: true });\n })\n .catch((err) => {\n console.error(err);\n setCreateLoading(false);\n });\n }\n\n return (\n
\n New Collection\n : null}\n />\n This will create a new collection in your workspace. You can edit settings\n and choose how to share it after it's creation.\n
\n );\n}\n\nexport default NewCollectionView;\n","import tw, {styled} from \"twin.macro\";\n\nimport {useCallback, useEffect} from \"react\";\n\nimport { BaseProps } from \"./BaseProps\";\n\nexport const Backdrop = tw.div`fixed top-0 left-0 w-full h-full`;\n\nexport interface ModalProps {\n className?: string;\n children?: JSX.Element | JSX.Element[] | null;\n centered?: boolean | null;\n onClose?: () => void | null;\n}\n\nconst ContentWrapper = styled.div((props: ModalProps) => [\n props.centered ? tw`inline-block absolute top-1/2 left-1/2 transform translate-x--1/2 translate-y--1/2 max-w-full overflow-auto` : tw`inline-block relative`]);\n\nexport const Modal = function (props: ModalProps): JSX.Element {\n const handleKeyPress = useCallback((event) => {\n event.key === \"Escape\" && props.onClose && props.onClose();\n }, [props]);\n\n useEffect(() => {\n document.addEventListener('keydown', handleKeyPress);\n return () => {\n document.removeEventListener('keydown', handleKeyPress);\n };\n }, [handleKeyPress]);\n\n return (\n
\n \n \n {props.children}\n \n {props.onClose && (\n
\n \n close\n
)}\n
\n );\n};\n\nexport interface IconProps {\n t: string;\n}\n\nexport function Icon(props: BaseProps & IconProps): JSX.Element {\n return \n}\n","import tw, { styled } from \"twin.macro\";\nimport { useState, useCallback, useRef, useEffect } from \"react\";\n\nimport { Tag, Label } from \"../../clients/apiClient\";\nimport { Icon } from \"../generic\";\nimport { BaseProps } from \"../generic/BaseProps\";\n\nexport interface LabelItemProps {\n isHighlight?: boolean;\n onDelete?: () => void;\n}\n\nexport function LabelItem(props: BaseProps & LabelItemProps) {\n return {props.children}\n {props.onDelete ? : null}\n \n}\n\nexport interface TagItemProps {\n isHighlight?: boolean;\n tag?: Tag;\n newValuesFn?: Function;\n}\n\nexport function TagItem({\n onClick,\n isHighlight,\n tag,\n newValuesFn = () => {},\n}: BaseProps & TagItemProps): JSX.Element {\n const [key, setKey] = useState(tag?.key || null);\n const [value, setValue] = useState(tag?.value || null);\n const [showValue, setShowValue] = useState(false);\n\n const inputRef = useRef(null);\n\n function handleChangeKey(event: any) {\n if (event.keyCode === 13) {\n const newKey = event.target.value;\n setKey(newKey);\n setShowValue(true);\n }\n if (event.target.value.includes(\":\")) {\n const newKey = event.target.value.replace(\":\", \"\");\n setKey(newKey);\n setShowValue(true);\n }\n }\n\n useEffect(() => {\n inputRef.current?.focus();\n }, [showValue]);\n\n function handleChangeValue(event: any) {\n if (event.keyCode === 13) {\n const newValue = event.target.value;\n setValue(newValue);\n saveNewAttribute(key, newValue);\n }\n }\n\n function saveNewAttribute(key?: string | null, value?: string | null) {\n if (key && value) {\n newValuesFn({ key, value });\n }\n }\n\n return (\n \n {key ? (\n \n {key}\n {\": \"}\n \n ) : (\n \n )}\n {value ? (\n \n {value}\n \n ) : key ? (\n \n ) : null}\n \n );\n}\n\nexport interface ItemSelection {\n item: Tag | Label;\n index: number;\n type: \"tag\" | \"label\";\n}\n\nexport interface ItemGroupProps {\n tags?: Tag[];\n labels?: Label[];\n onSelect?: (selection: ItemSelection) => void;\n}\n\nexport const ItemGroupContainer = styled.ul(({editMode}: {editMode?: boolean}) => [\n tw`flex flex-wrap gap-2 transition-all border border-transparent`,\n editMode && tw`border-grey m-[-6px] p-[6px]`,\n]);\n\nexport function ItemGroup({\n tags,\n labels,\n children,\n onSelect,\n}: BaseProps & ItemGroupProps) {\n const [, /*selectState*/ setSelectState] = useState({});\n const handleSelect = useCallback(\n (type: \"tag\" | \"label\", index: number, item: Tag | Label) => {\n setSelectState({ type, index, item });\n if (onSelect) {\n onSelect({ type, index, item });\n }\n },\n [onSelect]\n );\n\n return (\n \n {tags &&\n tags.map((t, idx) => (\n handleSelect(\"tag\", idx, t)} />\n ))}\n {labels &&\n labels.map((l, idx) => (\n handleSelect(\"label\", idx, l)}>\n {l}\n \n ))}\n {children}\n \n );\n}\n\nexport interface LabelsProps {\n labels?: Label[];\n onCreate?: (value: string) => void;\n onDelete?: (value: string) => void;\n}\n\nexport function Labels({ labels, onDelete, onCreate, className }: BaseProps & LabelsProps): JSX.Element {\n const [editMode, setEditMode] = useState(false);\n\n\n useEffect(() => {\n function handleClick() {\n setEditMode(false)\n }\n\n document.addEventListener('click', handleClick)\n return () => {\n document.removeEventListener('click', handleClick)\n }\n }, [editMode, setEditMode])\n\n const handleInput = useCallback((evt) => {\n if(evt.keyCode === 13) {\n evt.preventDefault();\n onCreate && onCreate(evt.target.value);\n // FIXME empty input on success\n }\n }, [onCreate])\n\n const handleEnterEditMode = useCallback((evt) => {\n evt.stopPropagation()\n setEditMode(true)\n }, [setEditMode])\n\n return (\n
\n \n {labels?.map(l => (\n onDelete(l) : undefined}\n >{l} \n ))}\n {onCreate ? (\n \n ): null}\n {onDelete && !editMode ? (\n \n ): null}\n \n
\n )\n}\n","import tw from \"twin.macro\";\n\nimport { useReducer, useCallback, useContext } from \"react\";\n\nimport { Tag, Label } from \"../../clients/apiClient\";\nimport ResourceInput from \"../generic/ResourceInput\";\n\nimport { TagItem, LabelItem, ItemGroup } from \"./Items\";\nimport { AlbumContext, CollectionContext, useAlbumHook } from \"../../clients/apiHooks\";\n\nconst Backdrop = tw.div`top-0 left-0 w-full h-full bg-white fixed`;\n\ninterface EditLabelProps {\n label?: string;\n onClose?: (evt: any) => void;\n onSubmit: (val: string) => void;\n}\nfunction EditLabel({ label, onClose, onSubmit }: EditLabelProps): JSX.Element {\n return (\n
\n \n {onClose && }\n
\n );\n}\n\ntype ComponentState = {\n mode: \"view\" | \"edit-tag\" | \"edit-label\" | \"add-tag\" | \"add-label\";\n isDirty: boolean;\n editTagIdx: number | null;\n editLabelIdx: number | null;\n tags?: Tag[];\n labels?: Label[];\n};\n\ntype ComponentStateUpdate = {\n mode?: \"view\" | \"edit-tag\" | \"edit-label\" | \"add-tag\" | \"add-label\";\n isDirty?: boolean;\n editTagIdx?: number | null;\n editLabelIdx?: number | null;\n tags?: Tag[];\n labels?: Label[];\n};\n\nconst initialState: ComponentState = {\n mode: \"view\",\n isDirty: false,\n editTagIdx: null,\n editLabelIdx: null,\n};\n\nfunction actionHandler(\n state: ComponentState,\n action: ComponentStateUpdate\n): ComponentState {\n const newState = { ...state, ...action };\n if (state.isDirty) {\n newState.tags = state.tags;\n newState.labels = state.labels;\n }\n return newState;\n}\n\nfunction useComponentState(tags: Tag[], labels: Label[]) {\n const [state, dispatch] = useReducer(actionHandler, initialState);\n\n return {\n enableEdit: useCallback((evt) => {\n evt.stopPropagation();\n //dispatch({ mode: 'edit' });\n }, []),\n disableEdit: useCallback((evt) => {\n evt.stopPropagation();\n dispatch({ mode: \"view\" });\n }, []),\n selectTag: useCallback((index, tags) => {\n dispatch({\n mode: \"edit-tag\",\n editTagIdx: index,\n tags,\n editLabelIdx: null,\n });\n }, []),\n selectLabel: useCallback((index, labels) => {\n dispatch({\n mode: \"edit-label\",\n editLabelIdx: index,\n labels,\n editTagIdx: null,\n });\n }, []),\n updateLabel: useCallback((val) => {\n dispatch({ mode: \"view\", isDirty: true }); //, newVal: val }); // FIXME\n }, []),\n isSelectedTag: useCallback((idx) => state.editTagIdx === idx, [state]),\n isSelectedLabel: useCallback((idx) => state.editLabelIdx === idx, [state]),\n\n labelToEdit: state.editLabelIdx != null && labels[state.editLabelIdx],\n tagToEdit: state.editTagIdx != null && tags[state.editTagIdx],\n\n mode: state.mode,\n isDirty: state.isDirty,\n tag: state.editTagIdx,\n tags: (state.isDirty ? state.tags : tags) || [],\n label: state.editLabelIdx,\n labels: (state.isDirty ? state.labels : labels) || [],\n };\n}\n\nexport interface TagsLabelsProps {\n tags?: Tag[];\n labels?: Label[];\n}\n\nfunction TagsLabels(props: TagsLabelsProps): JSX.Element {\n const state = useComponentState(props.tags || [], props.labels || []);\n const collectionId = useContext(CollectionContext);\n const albumId = useContext(AlbumContext);\n\n const { patchAlbum } = useAlbumHook(collectionId!, albumId!);\n\n const onKeyUp = (e: any) => {\n if (e.keyCode === 13) {\n const label = e.target.value;\n const new_vals = { labels: [...state.labels, label], tags: state.tags };\n patchAlbum(new_vals);\n e.target.value = \"\";\n }\n };\n\n return (\n
\n {state.mode !== \"view\" ? : null}\n \n \n
\n {state.tags.map((t, idx) => (\n state.selectTag(idx, props.tags)}\n />\n ))}\n {state.labels.map((l, idx) => (\n state.selectLabel(idx, props.labels)}\n >\n {l}\n \n ))}\n
\n
\n\n \n
  • \n + Add Tag Value\n
  • \n \n
    \n {state.labelToEdit && (\n \n )}\n {/* state.mode === 'edit-tag' && */}\n {state.mode === \"view\" && state.isDirty && (\n
    \n \n \n \n
    \n )}\n
    \n \n );\n}\n\nexport default TagsLabels;\n","import \"twin.macro\";\n\nimport React, { useContext } from \"react\";\nimport { Link } from \"react-router-dom\";\n\nimport { LazyLoadImage } from \"react-lazy-load-image-component\";\n\nimport {\n AlbumContext,\n CollectionContext,\n useSelectedAssets,\n} from \"../../clients/apiHooks\";\nimport { Asset } from \"../../clients/apiClient\";\n\nexport interface AssetTileProps {\n asset: Asset;\n}\n\nfunction AssetTile(props: AssetTileProps): JSX.Element | null {\n const asset = props.asset;\n\n const collectionId = useContext(CollectionContext);\n const albumId = useContext(AlbumContext);\n const { getAssetState } = useSelectedAssets();\n const assetState = getAssetState(asset.id);\n\n const link = `/collection/${collectionId}/a/${albumId}/s/${asset.id}`;\n\n if (!asset?.url) {\n return null;\n }\n\n if (asset.url.match(/(\\.jpg|\\.jpeg|\\.png|\\.pdf)$/i)) {\n return (\n \n \n
    {assetState.isSelected ? \"Selected\" : \"Not selected\"}
    \n \n );\n }\n\n return (\n \n {asset.url}\n \n );\n}\n\nexport default React.memo(AssetTile);\n","import { useState } from \"react\";\nimport { Document, Page } from \"react-pdf/dist/esm/entry.webpack\";\nimport \"react-pdf/dist/esm/Page/AnnotationLayer.css\";\nimport \"twin.macro\";\n\ninterface DocumentViewProps {\n fullUrl: string;\n}\n\nexport function DocumentView(props: DocumentViewProps): JSX.Element | null {\n const [numPages, setNumPages] = useState(null);\n const [pageNumber, setPageNumber] = useState(1);\n\n function onDocumentLoadSuccess({ numPages }: { numPages: number }) {\n setNumPages(numPages);\n setPageNumber(1);\n }\n\n function changePage(offset: number) {\n setPageNumber((prevPageNumber) => prevPageNumber + offset);\n }\n\n function previousPage() {\n changePage(-1);\n }\n\n function nextPage() {\n changePage(1);\n }\n\n function onItemClick(props: any) {\n setPageNumber(props.pageNumber);\n }\n\n return (\n
    \n \n \n \n
    \n

    \n Page {pageNumber || (numPages ? 1 : \"--\")} of {numPages || \"--\"}\n

    \n \n = (numPages || 1)}\n onClick={nextPage}\n >\n Next\n \n
    \n
    \n );\n}\n","import tw, { styled, css } from \"twin.macro\";\nimport { useCallback, useEffect, memo } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\n\nimport { AlbumWithAssets } from \"../../clients/apiClient\";\nimport {\n useAsset,\n useSelectedAssets,\n} from \"../../clients/apiHooks\";\n\nimport { TagItem, ItemGroupContainer, Labels } from \"../tags/Items\";\n\nimport { Icon } from \"../generic\";\nimport ErrorBoundary from \"../generic/ErrorBoundary\";\nimport { DocumentView } from \"./DocumentView\";\n\nconst ActionButton = styled.a([\n tw`font-bold px-8 py-4 inline-block rounded-full`,\n css`\n cursor: pointer;\n transition: all 0.3s;\n &:hover {\n background-color: #eee;\n }\n `,\n]);\n\nexport interface AssetDetailsProps {\n id: string;\n album?: AlbumWithAssets | null;\n className?: string;\n onSelect: () => void;\n onClose: () => void;\n}\n\nfunction AssetDetails(props: AssetDetailsProps): JSX.Element {\n const { toggleSelectAsset, getAssetState } = useSelectedAssets();\n const assetState = getAssetState(props.id);\n let pdfUrl: string | boolean = false;\n\n const { asset, deleteAsset, updateAssetMetadata } = useAsset({ assetId: props.id });\n\n const handleDelete = () => deleteAsset().then(props.onClose);\n\n const navigate = useNavigate();\n const handleKeyPress = useCallback(\n (event) => {\n const album = props.album;\n if (album?.assets?.length) {\n const currIdx = album?.assets?.findIndex(\n (ast: any) => ast.id === props.id\n );\n const prevIdx = currIdx === 0 ? album.assets.length - 1 : currIdx - 1;\n const nextIdx = currIdx === album.assets.length - 1 ? 0 : currIdx + 1;\n const prev = album.assets[prevIdx];\n const next = album.assets[nextIdx];\n event.key === \"ArrowRight\" &&\n navigate(\n `/collection/${album.collectionId}/a/${album.id}/s/${next.id}`\n );\n event.key === \"ArrowLeft\" &&\n navigate(\n `/collection/${album.collectionId}/a/${album.id}/s/${prev.id}`\n );\n }\n },\n [props, navigate]\n );\n\n useEffect(() => {\n // FIXME this is also triggered when moving a cursor inside an input...\n document.addEventListener(\"keydown\", handleKeyPress);\n return () => {\n document.removeEventListener(\"keydown\", handleKeyPress);\n };\n }, [handleKeyPress]);\n\n useEffect(() => {\n if (asset) {\n document.title = \"ExRepo - \" + asset.id;\n }\n });\n\n if (!asset) {\n return
    ;\n }\n\n if (asset.url.match(/(\\.pdf)$/i)) {\n pdfUrl = asset.url.replace(\"/i/\", \"/o/\");\n }\n\n function handleSelect() {\n toggleSelectAsset(props.id, assetState.isSelected);\n props.onSelect();\n }\n\n function handleNewLabel(newValue: string) {\n if (asset && newValue) {\n updateAssetMetadata({ labels: [...asset.labels, newValue] });\n }\n }\n function handleDeleteLabel(label: string) {\n if (asset && label) {\n updateAssetMetadata({ labels: asset.labels.filter(l => l !== label) });\n }\n }\n\n function handleNewTag({ key, value }: { key: string; value: string }) {\n if (!asset) {\n return;\n }\n const dataPayload = {\n tags: [...asset.tags, { key, value }],\n };\n updateAssetMetadata(dataPayload);\n }\n\n return (\n \n
    \n
    \n {pdfUrl ? (\n \n ) : (\n \n )}\n
    \n \n
    \n
    \n );\n}\n\nexport default memo(AssetDetails);\n","import { useUploads } from \"../../clients/apiHooks\";\n\ninterface uploadListProps {\n current: boolean;\n albumId: string;\n collectionId: string;\n}\n\nexport function UploadList({\n current,\n albumId,\n collectionId,\n}: uploadListProps) {\n const { atomFiles, removeFile } = useUploads(albumId, collectionId);\n const displayFiles = atomFiles.filter(f => {\n if (current) {\n return f.albumId === albumId && f.collectionId === collectionId\n } else {\n return f.albumId !== albumId || f.collectionId !== collectionId\n }\n });\n\n return (\n
    \n {displayFiles.map(f => (\n
    \n
    \n {f.file.name} {f.file.size} ({f.file.type}) ({f.state}){\" \"}\n {f.collectionId}/{f.albumId}\n
    \n removeFile(f.file.name)}\n >\n [RemoveFile]\n
    \n
    \n ))}\n
    \n );\n}\n","import \"twin.macro\";\nimport React from \"react\";\nimport { UploadList } from \"./UploadList\";\nimport { useUploads } from \"../../clients/apiHooks\";\n\nexport interface UploadsProps {\n albumId: string;\n collectionId: string;\n}\n\nexport function Uploads({\n albumId,\n collectionId,\n}: UploadsProps): JSX.Element | null {\n const { atomFiles, handleFileSelect, handleUpload } = useUploads(\n albumId,\n collectionId\n );\n\n return (\n
    \n
    \n
    \n \n
    \n {atomFiles.length} Files selected\n handleUpload(albumId, collectionId)}\n >\n Upload\n \n
    \n \n
    \n );\n}\n\nexport default React.memo(Uploads);\n","import \"twin.macro\";\nimport React, { useState } from \"react\";\nimport { useParams, useNavigate } from \"react-router-dom\";\n\nimport {\n CollectionContext,\n AlbumContext,\n useSelectedAssets,\n albumWithAssetsSelector,\n} from \"../../clients/apiHooks\";\n\nimport TagsLabels from \"../tags/TagsLabels\";\nimport AssetTile from \"./AssetTile\";\nimport AssetDetails from \"./AssetDetails\";\nimport Uploads from \"./Uploads\";\nimport { Modal } from \"../generic\";\nimport ErrorBoundary from \"../generic/ErrorBoundary\";\nimport { UploadList } from \"./UploadList\";\nimport { useRecoilValue } from \"recoil\";\n\nfunction AlbumView() {\n const { assetId, collectionId, id } = useParams<{\n id: string;\n collectionId: string;\n assetId: string;\n }>();\n const [showUpload /*, setShowUpload*/] = useState(false);\n const { getSelectedAssets } = useSelectedAssets();\n const selectedAssets = getSelectedAssets();\n\n const album = useRecoilValue(albumWithAssetsSelector({collectionId:collectionId!, albumId: id!}))\n const assets = album?.assets || [];\n\n const navigate = useNavigate();\n const handleCloseAssetDetails = () => {\n navigate(`/collection/${collectionId}/a/${id}`);\n };\n if (!album) {\n return null;\n }\n return (\n \n \n {selectedAssets.length ? (\n
    {selectedAssets.length} selected
    \n ) : null}\n \n {showUpload && (\n \n \n \n )}\n
    \n
    \n
    \n Album\n

    {album?.name || id}

    \n
    \n
    \n \n
    \n
    \n
    \n Tags & Labels\n \n
    \n
    \n Assets\n \n
    \n filter / sort / group by ...\n
    \n
    \n {assets.map((a) => (\n \n ))}\n
    \n
    \n
    \n {assetId && (\n \n \n \n )}\n
    \n
    \n
    \n );\n}\n\nexport default React.memo(AlbumView);\n","import \"twin.macro\";\n\nfunction NewAlbumView(): JSX.Element {\n return (\n
    \n New Album\n This will create a new album in the collection\n <COLLECTION_NAME_HERE>. You can add tags, labels and assets after\n it's creation.\n
    \n );\n}\n\nexport default NewAlbumView;\n","import { useMatch, Link } from \"react-router-dom\";\n\nimport { CollectionBase } from \"../../clients/apiClient\";\n\nexport interface CollectionListLinkProps {\n collection: CollectionBase;\n className?: string;\n}\n\nfunction CollectionListLink({\n collection,\n className,\n}: CollectionListLinkProps): JSX.Element {\n const target = `/collection/${collection.id}`;\n\n const routeMatch = useMatch({\n path: target,\n });\n return (\n
    \n \n
    \n {routeMatch && \">\"} {collection.name}\n
    \n \n
    \n );\n}\n\nexport default CollectionListLink;\n","import \"twin.macro\";\n\nimport { useEffect } from \"react\";\nimport { Link, useMatch, useNavigate } from \"react-router-dom\";\n\nimport { useWorkspace } from \"../../clients/apiHooks\";\n\nimport CollectionListLink from \"./CollectionListLink\";\n\nfunction CollectionList(): JSX.Element {\n const [collections, loading, err] = useWorkspace();\n const navigate = useNavigate();\n const routeMatch = useMatch({\n path: \"/\",\n });\n\n useEffect(() => {\n if (collections && collections.length && routeMatch) {\n navigate(`/collection/${collections[0].id}`, { replace: true });\n }\n }, [collections, navigate, routeMatch]);\n\n return (\n
    \n Collections\n \n \n \n {loading &&
    loading collections...
    }\n {err && (\n
    sorry, something went wrong fetching the collections...
    \n )}\n
    \n {collections &&\n collections.map((c) => (\n \n ))}\n
    \n
    \n );\n}\n\nexport default CollectionList;\n","import { useMatch, Link } from \"react-router-dom\";\n\nimport { Album } from \"../../clients/apiClient\";\n\nexport interface AlbumListLinkProps {\n album: Album;\n className?: string;\n}\n\nfunction AlbumListLink({ album, className }: AlbumListLinkProps): JSX.Element {\n const target = `/collection/${album.collectionId}/a/${album.id}`;\n\n const routeMatch = useMatch({\n path: target,\n });\n return (\n
    \n \n
    \n {routeMatch && \">\"} {album.name}\n
    \n \n
    \n );\n}\n\nexport default AlbumListLink;\n","import \"twin.macro\";\n\nimport { Link, useParams } from \"react-router-dom\";\nimport { albumsAtom, useCollection, useAlbumsHook } from \"../../clients/apiHooks\";\n\nimport AlbumListLink from \"./AlbumListLink\";\nimport { useRecoilValue } from \"recoil\";\n\nexport interface AlbumListProps {\n className?: string;\n}\n\nfunction AlbumList({ className }: AlbumListProps): JSX.Element {\n const { collectionId } = useParams<{ collectionId: string }>();\n const [collection, loading, err] = useCollection(collectionId);\n\n const albums = useRecoilValue(albumsAtom(collectionId!));\n\n const { createAlbum } = useAlbumsHook(collectionId!);\n\n const onKeyUp = (e: any) => {\n if (e.keyCode === 13) {\n createAlbum(e.target.value);\n e.target.value = \"\";\n }\n };\n\n return (\n
    \n
    \n
    \n \n << All Collections\n \n
    \n\n \n Collection\n

    \n {collection && collection.name}\n

    \n \n
    \n\n
    \n Albums\n {loading &&
    loading albums...
    }\n {err && (\n
    sorry, something went wrong fetching the collection...
    \n )}\n \n \n
    \n {\n [...albums]\n .sort((a, b) => a.id.localeCompare(b.id))\n .map((a) => )}\n
    \n
    \n
    \n );\n}\n\nexport default AlbumList;\n","import { Route, Routes } from \"react-router-dom\";\n\nimport CollectionList from \"./CollectionList\";\nimport AlbumList from \"./AlbumList\";\nimport { Suspense } from \"react\";\n\nfunction TopLevelNavigation({\n className,\n}: {\n className?: string;\n}): JSX.Element {\n return (\n
    \n \n }>}\n />\n } />\n } />\n \n
    \n );\n}\n\nexport default TopLevelNavigation;\n","import \"twin.macro\";\n\nimport { Link, Navigate } from \"react-router-dom\";\n\nfunction LandingPage(): JSX.Element {\n if (localStorage.getItem(\"token\")) {\n // TODO: also check if the token is valid before redirect + use apiClient instead inline code\n return ;\n }\n\n return (\n
    \n

    \n This is XR\n

    \n

    \n XR is the digital asset management platform for corporations,\n agencies and individual professionals.\n

    \n

    \n As your central storage, management and exchange facility, it's the\n single source of truth for all your files, product pictures, documents,\n photos and multi-media assets.\n

    \n

    \n At the same time, XR is built as your exchange hub with external\n partners and is designed to integrate seamlessly with your existing\n IT-ecosystem.\n

    \n\n

    Managing Assets

    \n

    \n XR allows you to organize your digital assets into collections of\n albums. This gives you sufficient granularity to divide and conquer huge\n amounts of files without getting lost in the ever so deep rabbit holes\n of folder structures.\n

    \n

    \n You can mark your folders as well as individual assets with tags or\n assign attributes to them, conveying additional meta information about\n the album or assets as necessary. In addition, XR allows you to\n enforce company wide rules for labels and attributes so that everyone is\n on the same page regarding your data.\n

    \n

    \n It includes a powerful search and filtering system that let's you easily\n and quickly find the exact assets that you're looking for.\n

    \n\n

    External Sharing

    \n

    \n Share individual assets, selections or entire albums internally in your\n organization, or with your external partners, be it creative agencies,\n customers or distributors.\n

    \n

    \n Shares are created and managed directly inside XR, so no need to\n copy files to a DropBox, WeTransfer, OneDrive, SharePoint or any other\n external system. Provide read-only or read/write access, with or without\n password protection.\n

    \n

    \n Once you've created a share for an external partner, you can keep\n reusing it by simply adding additional files and albums to it. Never\n again send links back and forth or forget what has been shared with whom\n with which link. Additionally, shares can be accessed via FTP or\n REST-API for a more programmatic and integrated communication scheme.\n

    \n\n

    Connecting other Solutions

    \n

    \n Connect XR to your PIM, eCommerce-solution or CMS to access your\n central asset storage directly, all without tedious and time-costly\n downloading, copying, renaming, or uploading thousands of files.\n

    \n

    \n Benefit from XR's powerful search and filtering and your own\n collection and album structure to quickly find the assets you need\n directly in your target system. If your system is capable, you can even\n auto-link assets to target entities based on asset labels and\n attributes.\n

    \n

    \n Ever wanted to switch your PIM, shop or WordPress to a different system\n or provider and had to somehow magically move all your assets? Not with{\" \"}\n XR! Just connect your target system with XR and continue\n where you left off.\n

    \n\n

    Technical Integration

    \n

    \n XR integrates with the most well known cloud provider storage\n backends (Azure, AWS, Google) as well as on-premise storage systems,\n this gives you the flexibility to store your collections wherever it\n suits you best!\n

    \n

    \n When it comes time to deliver assets to your users, XR plays well\n with modern frameworks like NextJs, Gatsby and many more. Assets are\n served through a state of the art CDN including on-demand resizing and\n re-formatting coupled with blazingly fast caching.\n

    \n

    \n To integrate well with your companies infrastructure, XR is also\n able to be accessed in NAS-fashion, as well as through a REST-API or\n FTP.\n

    \n\n

    \n \n \n \n

    \n
    \n );\n}\n\nexport default LandingPage;\n","import \"twin.macro\";\n\nimport { useCallback, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\n\nexport default function LoginView(): JSX.Element {\n const [value, setValue] = useState(localStorage.getItem(\"token\") || \"\");\n const navigate = useNavigate();\n\n const handleSubmit = useCallback(\n (evt: React.FormEvent) => {\n //TODO: Check if login is valid, if not dont navigate away\n evt.preventDefault();\n localStorage.setItem(\"token\", value);\n navigate(\"/collection/default\");\n // navigate(from, { replace: true }); TODO: Get the location from and redirect to last previous location for ease of use\n },\n [value, navigate]\n );\n\n return (\n
    \n
    \n ) =>\n setValue(evt.target.value)\n }\n tw=\"p-1 border border-black\"\n />\n \n \n
    \n );\n}\n","import \"twin.macro\";\nimport {\n BrowserRouter as Router,\n Navigate,\n Route,\n Routes,\n} from \"react-router-dom\";\n\nimport CollectionView from \"./components/collection/CollectionView\";\nimport NewCollectionView from \"./components/collection/NewCollectionView\";\nimport AlbumView from \"./components/album/AlbumView\";\nimport NewAlbumView from \"./components/album/NewAlbumView\";\nimport TopLevelNavigation from \"./components/navigation/TopLevelNavigation\";\nimport LandingView from \"./components/LandingView\";\nimport LoginView from \"./components/LoginView\";\nimport { RecoilRoot } from \"recoil\";\nimport { Suspense } from \"react\";\n\nfunction App() {\n return (\n \n
    \n \n \n }/>\n \n
    \n \n \n \n } />\n \n \n \n }\n />\n \n \n \n }\n />\n \n \n \n }\n />\n \n }>\n \n \n \n }\n />\n \n }>\n \n \n \n }\n />\n \n
    \n \n \n \n
    \n );\n}\n\nfunction PrivateRoute({ children }: any) {\n const token = localStorage.getItem(\"token\");\n return token ? children : ;\n}\n\nexport default App;\n","import { ReportHandler } from \"web-vitals\";\n\nconst reportWebVitals = (onPerfEntry?: ReportHandler) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import(\"web-vitals\").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport \"./index.css\";\nimport \"mono-icons/iconfont/icons.css\";\n\nimport { GlobalStyles } from \"twin.macro\";\nimport App from \"./App\";\nimport reportWebVitals from \"./reportWebVitals\";\n\nReactDOM.render(\n \n \n \n ,\n document.getElementById(\"root\")\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}