Use svelte instead
This commit is contained in:
parent
a53f5a2ad2
commit
3f453bc3b5
10 changed files with 769 additions and 1774 deletions
1
.gitignore → backend/.gitignore
vendored
1
.gitignore → backend/.gitignore
vendored
|
@ -1,3 +1,2 @@
|
|||
.env
|
||||
.venv/
|
||||
__pycache__/
|
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
|
@ -1,2 +1 @@
|
|||
node_modules/
|
||||
dist/
|
||||
|
|
|
@ -4,23 +4,18 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<style>
|
||||
@import "tailwindcss";
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { mount } from "svelte";
|
||||
import App from "./src/App.svelte";
|
||||
|
||||
import App from "./src/app.jsx";
|
||||
|
||||
createRoot(document.getElementById("root")).render(
|
||||
React.createElement(
|
||||
React.StrictMode,
|
||||
null,
|
||||
React.createElement(App, null),
|
||||
),
|
||||
);
|
||||
mount(App, { target: document.getElementById("app") });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
2333
frontend/package-lock.json
generated
2333
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,29 +1,22 @@
|
|||
{
|
||||
"name": "svelte-hello",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-youtube": "^10.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.3.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-css-order": "^2.1.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"svelte": "^5.25.9",
|
||||
"tailwindcss": "^4.1.3",
|
||||
"vite": "^6.2.5"
|
||||
},
|
||||
"prettier": {
|
||||
"plugins": [
|
||||
"prettier-plugin-css-order",
|
||||
"prettier-plugin-svelte",
|
||||
"prettier-plugin-tailwindcss"
|
||||
]
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"tailwindcss": {},
|
||||
"autoprefixer": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
65
frontend/src/App.svelte
Normal file
65
frontend/src/App.svelte
Normal file
|
@ -0,0 +1,65 @@
|
|||
<script>
|
||||
let channelName = "";
|
||||
let videoId = "";
|
||||
let loading = false;
|
||||
let error = "";
|
||||
|
||||
async function fetchVideo() {
|
||||
if (!channelName) return;
|
||||
loading = true;
|
||||
error = "";
|
||||
videoId = "";
|
||||
|
||||
try {
|
||||
const res = await fetch(`http://localhost:8000/video?channel_name=${channelName}`);
|
||||
if (!res.ok) throw new Error("Channel not found or API error");
|
||||
const data = await res.json();
|
||||
videoId = data.id;
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
fetchVideo();
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="min-h-screen bg-gray-100 flex flex-col items-center justify-center p-6">
|
||||
<h1 class="text-4xl font-extrabold mb-6">YouTube Latest Video Finder</h1>
|
||||
|
||||
<form on:submit={handleSubmit} class="flex flex-col sm:flex-row items-center gap-4">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={channelName}
|
||||
placeholder="Enter channel name"
|
||||
class="px-4 py-2 border border-gray-300 rounded-md w-72"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
|
||||
>
|
||||
{loading ? "Loading..." : "Get Latest Video"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{#if error}
|
||||
<p class="text-red-600 mt-4 text-sm">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if videoId}
|
||||
<div class="mt-8 w-full flex justify-center">
|
||||
<iframe
|
||||
title="video"
|
||||
width="560"
|
||||
height="315"
|
||||
src={`https://www.youtube.com/embed/${videoId}?rel=0`}
|
||||
class="rounded-lg shadow-lg"
|
||||
></iframe>
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
|
@ -1,76 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import YouTube from "react-youtube";
|
||||
|
||||
export default function App() {
|
||||
const [channelName, setChannelName] = useState("");
|
||||
const [videoId, setVideoId] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
async function fetchLatestVideo() {
|
||||
if (!channelName) return;
|
||||
setLoading(true);
|
||||
setError("");
|
||||
setVideoId(null);
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`http://localhost:8000/video?channel_name=${channelName}`,
|
||||
);
|
||||
if (!res.ok) throw new Error("Channel not found or API error");
|
||||
const data = await res.json();
|
||||
setVideoId(data.id);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const playerOptions = {
|
||||
playerVars: {
|
||||
autoplay: 0,
|
||||
rel: 0,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-100 p-6">
|
||||
<h1 className="mb-6 text-4xl font-extrabold">
|
||||
YouTube Latest Video Finder
|
||||
</h1>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
fetchLatestVideo();
|
||||
}}
|
||||
className="flex flex-col items-center gap-4 sm:flex-row"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value={channelName}
|
||||
onChange={(e) => setChannelName(e.target.value)}
|
||||
placeholder="Enter channel name"
|
||||
className="w-72 rounded-md border border-gray-300 px-4 py-2"
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
|
||||
>
|
||||
{loading ? "Loading..." : "Get Latest Video"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
||||
|
||||
{videoId && (
|
||||
<div className="mt-8 flex w-full justify-center">
|
||||
<YouTube videoId={videoId} opts={playerOptions} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -1,8 +0,0 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.jsx"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
|
@ -1,7 +1,11 @@
|
|||
import tailwindcss from "@tailwindcss/vite"
|
||||
import { defineConfig } from "vite";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
|
||||
export default defineConfig({
|
||||
esbuild: {
|
||||
jsxInject: `import React from 'react'`,
|
||||
},
|
||||
plugins: [
|
||||
svelte(),
|
||||
tailwindcss()
|
||||
],
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue