From 61376351e224039fd86c53455982b8b21b7975b9 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 14 Sep 2025 11:20:17 +0000 Subject: [PATCH] (add+update) : UI faite avec tailwind + connexion l'API) --- package.json | 8 +- postcss.config.js | 6 ++ src/App.tsx | 40 ++------ src/components/layout/Layout.tsx | 11 ++ src/features/youtube/YoutubeForm.tsx | 144 +++++++++++++++++++++++++++ src/index.css | 68 ------------- src/main.tsx | 10 +- src/pages/Home.tsx | 9 ++ src/pages/Login.tsx | 0 src/pages/NotFound.tsx | 0 src/services/apiClient.ts | 25 +++++ src/services/jellyfinApi.ts | 0 src/styles/index.css | 25 +++++ src/types/api.ts | 5 + src/types/global.d.ts | 0 tailwind.config.js | 28 ++++++ 16 files changed, 276 insertions(+), 103 deletions(-) create mode 100644 postcss.config.js create mode 100644 src/components/layout/Layout.tsx create mode 100644 src/features/youtube/YoutubeForm.tsx delete mode 100644 src/index.css delete mode 100644 src/pages/Login.tsx delete mode 100644 src/pages/NotFound.tsx create mode 100644 src/services/apiClient.ts delete mode 100644 src/services/jellyfinApi.ts create mode 100644 src/styles/index.css create mode 100644 src/types/api.ts delete mode 100644 src/types/global.d.ts create mode 100644 tailwind.config.js diff --git a/package.json b/package.json index 82177c4..f185980 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", @@ -12,17 +12,21 @@ }, "dependencies": { "react": "^19.1.1", - "react-dom": "^19.1.1" + "react-dom": "^19.1.1", + "react-router-dom": "^7.9.1" }, "devDependencies": { "@eslint/js": "^9.33.0", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", "@vitejs/plugin-react-swc": "^4.0.0", + "autoprefixer": "^10.4.21", "eslint": "^9.33.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", + "postcss": "^8.5.6", + "tailwindcss": "3.4.17", "typescript": "~5.8.3", "typescript-eslint": "^8.39.1", "vite": "^7.1.2" diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/App.tsx b/src/App.tsx index 3d7ded3..6fa3df6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,15 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import { Routes, Route } from "react-router-dom"; +import Home from "./pages/Home"; +import Layout from "./components/layout/Layout"; function App() { - const [count, setCount] = useState(0) - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) + + }> + } /> + + + ); } -export default App +export default App; \ No newline at end of file diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx new file mode 100644 index 0000000..4011487 --- /dev/null +++ b/src/components/layout/Layout.tsx @@ -0,0 +1,11 @@ +import { Outlet } from "react-router-dom"; + +export default function Layout() { + return ( +
+
+ +
+
+ ); +} \ No newline at end of file diff --git a/src/features/youtube/YoutubeForm.tsx b/src/features/youtube/YoutubeForm.tsx new file mode 100644 index 0000000..3d2a23b --- /dev/null +++ b/src/features/youtube/YoutubeForm.tsx @@ -0,0 +1,144 @@ +import { useState } from "react"; +import { sendYoutubeUrl } from "../../services/apiClient"; + +interface VideoJob { + id: string; + url: string; + format: string; + status: "pending" | "processing" | "done" | "error"; +} + +export default function YoutubeForm() { + const [url, setUrl] = useState(""); + const [format, setFormat] = useState("mp3"); // format par défaut + const [loading, setLoading] = useState(false); + const [message, setMessage] = useState(null); + const [jobs, setJobs] = useState([]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setMessage(null); + + try { + const res = await sendYoutubeUrl(url, format); + const newJob: VideoJob = { + id: res.jobId || Date.now().toString(), + url, + format, + status: "processing", + }; + setJobs((prev) => [newJob, ...prev]); + setMessage(res.message || "Vidéo envoyée avec succès !"); + setUrl(""); + } catch (err: any) { + setMessage(err.message || "Erreur lors de l’envoi."); + } finally { + setLoading(false); + } + }; + + const clearHistory = () => { + setJobs([]); + setMessage("Historique vidé ✅"); + }; + + return ( +
+

+ Youtube To Jellyfin +

+

+ Collez un lien YouTube ci-dessous et choisissez le format. La vidéo sera + téléchargée, convertie et envoyée automatiquement sur votre serveur. +

+ +
+ setUrl(e.target.value)} + className="flex-1 px-4 py-3 rounded-lg bg-surfaceLight text-text + placeholder-gray-500 border border-gray-700 + focus:outline-none focus:ring-2 focus:ring-primary" + /> + + + + +
+ + {message && ( +

{message}

+ )} + + {jobs.length > 0 && ( +
+
+

Vidéos envoyées

+ +
+ +
    + {jobs.map((job) => ( +
  • + + {job.url} ({job.format}) + + + {job.status} + +
  • + ))} +
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 08a3ac9..0000000 --- a/src/index.css +++ /dev/null @@ -1,68 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/src/main.tsx b/src/main.tsx index bef5202..c3669e1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,14 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' -import './index.css' + +import { BrowserRouter } from 'react-router-dom' import App from './App.tsx' +import "./styles/index.css"; createRoot(document.getElementById('root')!).render( - - , + + + + ) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index e69de29..f4ab315 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -0,0 +1,9 @@ +import YoutubeForm from "../features/youtube/YoutubeForm"; + +export default function Home() { + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts new file mode 100644 index 0000000..049d863 --- /dev/null +++ b/src/services/apiClient.ts @@ -0,0 +1,25 @@ +export interface ApiResponse { + success: boolean; + message: string; + jobId?: string; +} + +const API_URL = import.meta.env.VITE_API_URL; +const API_KEY = import.meta.env.VITE_API_KEY; + +export async function sendYoutubeUrl(url: string, format: string): Promise { + const res = await fetch(`${API_URL}/api/download`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${API_KEY}`, + }, + body: JSON.stringify({ url, format }), + }); + + if (!res.ok) { + throw new Error(`Erreur serveur : ${res.status}`); + } + + return res.json(); +} \ No newline at end of file diff --git a/src/services/jellyfinApi.ts b/src/services/jellyfinApi.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/styles/index.css b/src/styles/index.css new file mode 100644 index 0000000..9c5f721 --- /dev/null +++ b/src/styles/index.css @@ -0,0 +1,25 @@ +/* eslint-disable @tailwindcss/no-custom-at-rules */ +html, body, #root { + height: 100%; + margin: 0; + padding: 0; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fadeIn { + animation: fadeIn 0.4s ease-out forwards; +} + +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 0000000..40d4136 --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,5 @@ +export interface ApiResponse { + success: boolean; + message: string; + jobId?: string; // si tu veux suivre le téléchargement +} \ No newline at end of file diff --git a/src/types/global.d.ts b/src/types/global.d.ts deleted file mode 100644 index e69de29..0000000 diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..0f2d896 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,28 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + container: { + center: true, + padding: "1rem", + }, + colors: { + background: "#121212", + surface: "#1E1E1E", + surfaceLight: "#2A2A2A", + primary: "#BB86FC", + secondary: "#03DAC6", + text: { + DEFAULT: "#E0E0E0", + muted: "#A0A0A0", + }, + }, + + }, + }, + plugins: [], +} \ No newline at end of file