diff --git a/frontend/.env.sample b/frontend/.env.sample new file mode 100644 index 0000000..83dfbb8 --- /dev/null +++ b/frontend/.env.sample @@ -0,0 +1 @@ +API_URL=https://example.com \ No newline at end of file diff --git a/frontend/astro.config.mjs b/frontend/astro.config.mjs index 227a8b7..54bd0e5 100644 --- a/frontend/astro.config.mjs +++ b/frontend/astro.config.mjs @@ -1,14 +1,17 @@ import { defineConfig } from "astro/config"; import node from "@astrojs/node"; +import react from "@astrojs/react"; + // https://astro.build/config export default defineConfig({ site: "https://jojo.schibsted.com", output: "server", adapter: node({ - mode: "standalone", + mode: "standalone" }), prefetch: { - prefetchAll: true, + prefetchAll: true }, -}); + integrations: [react()] +}); \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 84a2d67..ae1b2ae 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,7 +9,14 @@ "dependencies": { "@astrojs/check": "^0.3.3", "@astrojs/node": "^7.0.2", + "@astrojs/react": "^3.0.10", + "@nanostores/react": "^0.7.2", + "@types/react": "^18.2.64", + "@types/react-dom": "^18.2.21", "astro": "^4.0.7", + "nanostores": "^0.10.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "typescript": "^5.3.3", "zod": "^3.22.4" } @@ -138,6 +145,24 @@ "node": ">=18.14.1" } }, + "node_modules/@astrojs/react": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-3.0.10.tgz", + "integrity": "sha512-uGRIwKMAn7tva2vxXMyoVIGxWFr0rjZ8ZWIlkTG/vIpnAjD2nM8Cz6B8j7yzj176jvl6gZ6xTbTVPm09aeK0Yw==", + "dependencies": { + "@vitejs/plugin-react": "^4.2.0", + "ultrahtml": "^1.3.0" + }, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "@types/react": "^17.0.50 || ^18.0.21", + "@types/react-dom": "^17.0.17 || ^18.0.6", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.4.tgz", @@ -457,6 +482,34 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -912,6 +965,24 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nanostores/react": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@nanostores/react/-/react-0.7.2.tgz", + "integrity": "sha512-e3OhHJFv3NMSFYDgREdlAQqkyBTHJM91s31kOZ4OvZwJKdFk5BLk0MLbh51EOGUz9QGX2aCHfy1RvweSi7fgwA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "nanostores": "^0.9.0 || ^0.10.0", + "react": ">=18.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1184,6 +1255,34 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + }, + "node_modules/@types/react": { + "version": "18.2.64", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.64.tgz", + "integrity": "sha512-MlmPvHgjj2p3vZaxbQgFUQFvD8QiZwACfGqEdDSWou5yISWxDQ4/74nCAwsUiX7UFLKZz3BbVSPj+YxeoGGCfg==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.21", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.21.tgz", + "integrity": "sha512-gnvBA/21SA4xxqNXEwNiVcP0xSGHh/gi1VhWv9Bl46a0ItbTT5nFY+G9VSQpaG/8N/qdJpJ+vftQ4zflTtnjLw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" + }, "node_modules/@types/unist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", @@ -1194,6 +1293,24 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, + "node_modules/@vitejs/plugin-react": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", + "dependencies": { + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, "node_modules/@volar/kit": { "version": "1.10.10", "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-1.10.10.tgz", @@ -2000,6 +2117,11 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3135,6 +3257,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4020,6 +4153,20 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nanostores": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.10.0.tgz", + "integrity": "sha512-Poy5+9wFXOD0jAstn4kv9n686U2BFw48z/W8lms8cS8lcbRz7BU20JxZ3e/kkKQVfRrkm4yLWCUA6GQINdvJCQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -4674,6 +4821,37 @@ "rc": "cli.js" } }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -5383,6 +5561,14 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -5925,6 +6111,11 @@ "semver": "^7.3.8" } }, + "node_modules/ultrahtml": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.3.tgz", + "integrity": "sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==" + }, "node_modules/unherit": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/unherit/-/unherit-3.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5de93fb..887bda6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,14 @@ "dependencies": { "@astrojs/check": "^0.3.3", "@astrojs/node": "^7.0.2", + "@astrojs/react": "^3.0.10", + "@nanostores/react": "^0.7.2", + "@types/react": "^18.2.64", + "@types/react-dom": "^18.2.21", "astro": "^4.0.7", + "nanostores": "^0.10.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "typescript": "^5.3.3", "zod": "^3.22.4" } diff --git a/frontend/src/components/Modal/LanguageSettings.tsx b/frontend/src/components/Modal/LanguageSettings.tsx new file mode 100644 index 0000000..34e57b6 --- /dev/null +++ b/frontend/src/components/Modal/LanguageSettings.tsx @@ -0,0 +1,59 @@ +import { useStore } from "@nanostores/react"; +import { $modelSettings, cancelUpload, setSelectedLanguage } from "../../stores/FileStore"; + +const LanguageSettings = ({ + languages, + setSettingTab, + files, +}: { + languages: string[]; + setSettingTab: (tab: number) => void; + files: { files: File[] }; + +}) => { + const modelSettings = useStore($modelSettings); + + const { selectedLanguage } = modelSettings; + + return ( +
+ +

Select language

+

+ Choose the main language that is spoken in the file, to help us get the best + transcription. +

+
+

{files.files.map(file => file.name).join(', ')}

+
+ + +
+ ); +}; + +export default LanguageSettings; diff --git a/frontend/src/components/Modal/UploadModal.tsx b/frontend/src/components/Modal/UploadModal.tsx new file mode 100644 index 0000000..b31e8b8 --- /dev/null +++ b/frontend/src/components/Modal/UploadModal.tsx @@ -0,0 +1,31 @@ +import { useStore } from "@nanostores/react"; +import { $fileStore } from "../../stores/FileStore"; +import { useState } from "react"; +import LanguageSettings from "./LanguageSettings"; +import './styles.css'; + +const UploadModal = ({ languages, model }: {languages: string[], model: string}) => { + const [step, setStep] = useState(0); + + const files = useStore($fileStore); + + if(!files.files){ + return null; + } + + if (files.files && files.files.length > 0 && step === 0) { + return ( + + ); + } + + + + return
hei
; +}; + +export default UploadModal; diff --git a/frontend/src/components/Modal/styles.css b/frontend/src/components/Modal/styles.css new file mode 100644 index 0000000..078c208 --- /dev/null +++ b/frontend/src/components/Modal/styles.css @@ -0,0 +1,532 @@ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + } + + h1, + p, + body { + margin: 0; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; + } + + body { + min-height: 100vh; + display: flex; + flex: 1 1 auto; + flex-direction: column; + font-weight: 400; + font-size: 0.875rem; + } + + .stats { + color: #fff; + font-weight: bold; + font-size: 2.5rem; + position: absolute; + top: 40%; + width: 100%; + text-align: center; + } + + @media (max-width: 1100px) { + .stats { + display: none; + } + } + + .app { + display: flex; + flex: 1 1 auto; + background-size: cover; + background-position: center; + display: flex; + flex-flow: column; + max-height: 100vh; + } + + .header { + color: #ffffff; + font-weight: 400; + font-size: 4rem; + padding: 32px; + display: flex; + justify-content: space-between; + position: relative; + } + + .image-credit { + font-size: 1rem; + font-weight: 400; + color: rgba(255, 255, 255, 0.7); + writing-mode: vertical-rl; + transform: rotate(-180deg); + position: fixed; + right: 12px; + bottom: 20px; + white-space: nowrap; + } + + .isDragging * { + pointer-events: none; + } + + .main { + flex: 1 1 auto; + display: flex; + flex-direction: column; + color: #fff; + position: absolute; + bottom: 32px; + } + + .upload-form { + display: flex; + flex-direction: column; + gap: 16px; + align-self: center; + background-color: rgba(255, 255, 255, 0.1); + border-radius: 15px; + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + padding: 24px; + max-width: 100%; + height: 100%; + margin: 0 32px; + } + + .upload-form a { + font-weight: bold; + color: #fff; + } + + .upload-form p:last-of-type { + margin-bottom: 0; + } + + .upload-form legend { + display: flex; + flex-direction: column; + gap: 12px; + } + + .upload-form h1 { + font-weight: 600; + font-size: 1.25rem; + line-height: 24px; + } + + @media (min-width: 480px) { + .main { + max-width: 390px; + align-self: flex-start; + } + } + + .dropzone { + width: 100%; + height: 125px; + border-radius: 4px; + background: rgba(255, 255, 255, 0.17); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + .dropzone span { + color: #fff; + font-weight: 400; + font-size: 14px; + line-height: 17px; + } + + .file-upload-button { + width: 100%; + height: 40px; + background: inherit; + border-radius: 8px; + border: none; + color: inherit; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + font-weight: 500; + font-size: 1rem; + color: #fff; + } + + .drag-drop-overlay { + display: flex; + flex: 1 1 auto; + flex-direction: column; + justify-content: center; + width: 100%; + text-align: center; + color: #fff; + } + + .drag-drop-overlay h1 { + font-size: 3.5rem; + font-weight: 800; + } + + .drag-drop-overlay p { + font-size: 1.5rem; + font-weight: 400; + } + + .settings, + .editor { + position: relative; + align-self: center; + max-width: 350px; + color: #fff; + background: rgba(255, 255, 255, 0.1); + margin: auto 0; + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + padding: 24px; + border-radius: 15px; + } + + .settings h1 { + font-size: 1.25rem; + font-weight: 600; + } + + .language-description { + font-size: 0.825rem; + font-weight: 400; + margin: 8px 0px; + } + + .button-cancel { + position: absolute; + right: 0; + top: 0; + border: none; + background: none; + padding: 8px; + cursor: pointer; + } + + .file-info { + display: flex; + flex-direction: row; + align-items: center; + border: 2px solid rgba(255, 255, 255, 0.17); + padding: 2px 4px; + border-radius: 4px; + gap: 4px; + } + + .file-info svg { + flex: none; + } + + .file-info p { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .email-input { + margin: 16px 0; + display: flex; + flex-direction: column; + gap: 8px; + } + + .email-input label { + font-size: 1rem; + font-weight: 400; + } + + .email-description { + font-size: 0.825rem; + font-weight: 400; + margin: 8px 0px; + } + + .email-input input { + padding: 10px 12px; + border-radius: 8px; + border: none; + background: rgba(255, 255, 255, 0.17); + backdrop-filter: blur(2px); + -webkit-backdrop-filter: blur(2px); + color: #fff; + font-weight: 500; + font-size: 1rem; + } + + .email-input input::placeholder { + color: #fff; + } + + .select-dropdown { + padding: 10px 12px; + margin: 16px 0; + border-radius: 8px; + border: none; + background: rgba(255, 255, 255, 0.17); + backdrop-filter: blur(2px); + -webkit-backdrop-filter: blur(2px); + color: #fff; + font-weight: 500; + font-size: 1rem; + width: 100%; + cursor: pointer; + text-transform: capitalize; + } + + .button-upload, + .editor button { + margin: 0 0 16px 0; + padding: 10px 12px; + border-radius: 8px; + border: none; + background: rgba(255, 255, 255, 0.17); + backdrop-filter: blur(2px); + -webkit-backdrop-filter: blur(2px); + color: #fff; + font-weight: 500; + font-size: 1rem; + width: 100%; + cursor: pointer; + } + + .button-upload:disabled { + background: rgba(255, 255, 255, 0.1); + cursor: not-allowed; + } + + .error { + display: flex; + flex: 1 1 auto; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + text-align: center; + color: #fff; + } + + .error h1 { + font-size: 3.5rem; + font-weight: 800; + } + + .error p { + font-size: 1.5rem; + font-weight: 400; + } + + .error button { + padding: 8px 26px; + border-radius: 8px; + border: none; + background: rgba(255, 255, 255, 0.17); + backdrop-filter: blur(2px); + -webkit-backdrop-filter: blur(2px); + margin: 16px 0px; + color: #fff; + font-weight: 500; + font-size: 1rem; + cursor: pointer; + } + + .no-script { + display: flex; + flex: 1 1 auto; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + text-align: center; + color: #fff; + background-color: #dd0000; + } + + .no-script h1 { + font-size: 3.5rem; + font-weight: 800; + } + + .no-script p { + font-size: 1.5rem; + font-weight: 400; + } + + option { + color: black; + } + + .position-title { + font-weight: 600; + font-size: 1rem; + margin: 0; + } + + .spinner { + align-self: center; + width: 80px; + height: 80px; + } + + .spinner div { + transform-origin: 40px 40px; + animation: spinner 1.2s linear infinite; + } + + .spinner div:after { + content: " "; + display: block; + position: absolute; + top: 3px; + left: 37px; + width: 6px; + height: 18px; + border-radius: 20%; + background: #fff; + } + .spinner div:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; + } + .spinner div:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; + } + .spinner div:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; + } + .spinner div:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; + } + .spinner div:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; + } + .spinner div:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; + } + .spinner div:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; + } + .spinner div:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; + } + .spinner div:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; + } + .spinner div:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; + } + .spinner div:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; + } + .spinner div:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; + } + @keyframes spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + + #transcription tr td { + outline: none; + } + + .play-button-cell { + max-width: 30px; + } + + #transcription tr td:first-child { + border-left: 3px solid #fff; + } + + #transcription tr:hover td:first-child { + border-left: 3px solid #eee; + } + + #transcription tr.selected td:first-child { + border-left: 3px solid blue; + } + + svg.play-icon:hover { + fill: #808080; + cursor: pointer; + } + + .table-container { + overflow-y: scroll; + max-height: 100vh; + background-color: #fff; + display: inline-block; + border-radius: 15px; + } + + .editor { + margin: 10px; + width: auto; + text-align: center; + } + + #save div { + display: none; + } + + #save:hover div { + display: block; + } + + #save > button:after { + content: ""; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 10px solid rgba(255, 255, 255, 0.5); + display: inline-block; + margin-left: 10px; + } + + #save:hover > button:after { + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-bottom: 10px solid rgba(255, 255, 255, 0.5); + border-top: 0; + } + + @media (min-width: 850px) { + .editor { + position: fixed; + top: 15px; + right: 15px; + width: 300px; + } + } + \ No newline at end of file diff --git a/frontend/src/components/Stats.astro b/frontend/src/components/Stats.astro index 84f4a04..a84d67d 100644 --- a/frontend/src/components/Stats.astro +++ b/frontend/src/components/Stats.astro @@ -1,7 +1,9 @@ --- import z from "zod"; -const response = await fetch("http://api:3000/v1/stats"); +const baseApiUrl = import.meta.env.API_URL + +const response = await fetch(`${baseApiUrl}/v1/stats`); const data = await response.json(); const transcriptionTimeMultiplier = 4; diff --git a/frontend/src/components/Upload.astro b/frontend/src/components/Upload.astro index 9e1f79a..531ac23 100644 --- a/frontend/src/components/Upload.astro +++ b/frontend/src/components/Upload.astro @@ -44,6 +44,7 @@ const { accentColor } = Astro.props; diff --git a/frontend/src/pages/index.astro b/frontend/src/pages/index.astro index 8d5634e..3a45193 100644 --- a/frontend/src/pages/index.astro +++ b/frontend/src/pages/index.astro @@ -5,8 +5,43 @@ import Stats from "../components/Stats.astro"; import Upload from "../components/Upload.astro"; import ImageCredits from "../components/ImageCredits.astro"; import { images } from "../components/BackgroundImages/credits"; +import UploadModal from "../components/Modal/UploadModal"; const image = images[Math.floor(Math.random() * images.length)]!; + +const fetchOptions = async () => { + try { + const response = await fetch(`${import.meta.env.API_URL}/v1/transcribe`, { + method: "OPTIONS", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + const {queryParams} = data; + const {languages, model} = queryParams; + + const topLanguages = ["norwegian", "swedish", "english"]; + const otherLanguages = languages.options.filter( + (lang: string) => !topLanguages.includes(lang) + ); + + return { + languages: [...topLanguages, ...otherLanguages.sort()], + model, + } + + } catch (error) { + console.error("Error fetching options", error); + return { + languages: ["norwegian", "swedish", "english"], + model: "default", + }; + } +}; + +const {languages, model} = await fetchOptions(); --- @@ -15,7 +50,7 @@ const image = images[Math.floor(Math.random() * images.length)]!;
- +
@@ -42,4 +77,4 @@ const image = images[Math.floor(Math.random() * images.length)]!; bottom: 32px; right: 12px; } - + \ No newline at end of file diff --git a/frontend/src/stores/FileStore.ts b/frontend/src/stores/FileStore.ts new file mode 100644 index 0000000..978a68a --- /dev/null +++ b/frontend/src/stores/FileStore.ts @@ -0,0 +1,46 @@ +import { atom } from "nanostores"; + +interface FileState { + files: File[] | null; + error: string | null; +} + +const initialFileState: FileState = { + files: null, + error: null, +}; + +export const $fileStore = atom(initialFileState); + +export const setFiles = (files: File[]) => { + $fileStore.set({...$fileStore.get(), files}) +} + +export const setError = (error: string) => { + $fileStore.set({...$fileStore.get(), error}) +}; + +export const cancelUpload = () => { + $fileStore.set(initialFileState); +} + + +interface ModelSettings { + selectedLanguage: string; + selectedModel: string; +} + +const initialModelSettings: ModelSettings = { + selectedLanguage: "", + selectedModel: "large" +} + +export const $modelSettings = atom(initialModelSettings) + +export const setSelectedLanguage = (language: string) => { + $modelSettings.set({...$modelSettings.get(), selectedLanguage: language}) +} + +export const setSelectedModel = (model: string) => { + $modelSettings.set({...$modelSettings.get(), selectedModel: model}) +} \ No newline at end of file diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 3fd7ae6..a3b8ae1 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,3 +1,7 @@ { - "extends": "astro/tsconfigs/strictest" + "extends": "astro/tsconfigs/strictest", + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react" + } } \ No newline at end of file