feat(frontend): restyle and design frontend
This commit is contained in:
@@ -7,7 +7,7 @@ type props = {
|
||||
|
||||
const Box = ({children, className}: props) => {
|
||||
return (
|
||||
<div className={clsx("bg-gray-200 shadow-lg rounded-lg", className)}>
|
||||
<div className={clsx("bg-white border border-gray-200 shadow-sm rounded-lg", className)}>
|
||||
{ children }
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ type DropdownItemProps = {
|
||||
|
||||
const DropdownItem = ({ item, onClick, className }: DropdownItemProps) => {
|
||||
return (
|
||||
<li className={clsx(className, "cursor-pointer hover:bg-gray-100 px-4 py-2 align-middle")}
|
||||
<li className={clsx("cursor-pointer hover:bg-gray-50 px-3 py-2 text-sm text-gray-700", className)}
|
||||
onClick={() => onClick(item)}
|
||||
>
|
||||
{item}
|
||||
@@ -49,12 +49,12 @@ const Dropdown = ({ label, children, className }: DropdownProps) => {
|
||||
ref={ref}>
|
||||
<button
|
||||
onClick={toggleDropdown}
|
||||
className={"inline-flex justify-between w-full rounded-xl px-4 py-2 bg-white text-sm font-medium text-gray-600 hover:bg-gray-50"}
|
||||
className="inline-flex items-center gap-1 rounded-lg px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 transition-colors duration-150"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
{isOpen && (
|
||||
<ul className={"absolute w-30 origin-top-right rounded-md bg-white shadow-lg font-medium"}>
|
||||
<ul className="absolute right-0 mt-1 w-36 origin-top-right rounded-lg bg-white border border-gray-200 shadow-md z-50 py-1 overflow-hidden">
|
||||
{children}
|
||||
</ul>
|
||||
)}
|
||||
@@ -64,4 +64,4 @@ const Dropdown = ({ label, children, className }: DropdownProps) => {
|
||||
|
||||
|
||||
|
||||
export { Dropdown, DropdownItem };
|
||||
export { Dropdown, DropdownItem };
|
||||
|
||||
@@ -8,11 +8,10 @@ type props = {
|
||||
const Selector = ({children, label}: props) => {
|
||||
return (
|
||||
<div className={"flex items-center gap-2"}>
|
||||
<label
|
||||
className={"w-full"}>
|
||||
<label className={"w-full text-sm text-gray-600"}>
|
||||
{ label }
|
||||
</label>
|
||||
<div className={"w-px h-6 bg-gray-400 mx-3"} />
|
||||
<div className={"w-px h-5 bg-gray-200 mx-2"} />
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import clsx from "clsx";
|
||||
import SidebarButton from "./buttons/SidebarButton.tsx";
|
||||
import { Plus, Film, Home, User as UserIcon } from 'lucide-react';
|
||||
import Box from "./Box.tsx";
|
||||
import type {User} from "../utils/types.ts";
|
||||
|
||||
type props = {
|
||||
@@ -11,40 +10,26 @@ type props = {
|
||||
|
||||
const Sidebar = ({user, className}: props) => {
|
||||
return (
|
||||
<Box className={clsx("h-screen flex flex-col mr-5", className)}>
|
||||
<img
|
||||
className={"w-45 mx-auto"}
|
||||
src={"../../public/logo.png"}
|
||||
alt={"VoD System Logo"}
|
||||
/>
|
||||
<SidebarButton
|
||||
url={"/"}
|
||||
logo={<Home size={20}/>}
|
||||
label={"Home"}
|
||||
/>
|
||||
|
||||
<SidebarButton
|
||||
url={"/create"}
|
||||
logo={<Plus size={20}/>}
|
||||
label={"Create Clip"}
|
||||
/>
|
||||
|
||||
{ user && (
|
||||
<>
|
||||
<SidebarButton
|
||||
url={"/my-clips"}
|
||||
logo={<Film size={20}/>}
|
||||
label={"My Clips"}
|
||||
/>
|
||||
<SidebarButton
|
||||
url={"/profile"}
|
||||
logo={<UserIcon size={20}/>}
|
||||
label={"Profile"}
|
||||
/>
|
||||
</>
|
||||
<aside className={clsx("h-screen flex flex-col bg-white border-r border-gray-200", className)}>
|
||||
<div className="px-4 pt-5 pb-3">
|
||||
<img
|
||||
className="w-36 mx-auto"
|
||||
src="../../public/logo.png"
|
||||
alt="VoD System Logo"
|
||||
/>
|
||||
</div>
|
||||
<nav className="flex flex-col flex-1 gap-0.5 px-2 pb-4">
|
||||
<SidebarButton url="/" logo={<Home size={18}/>} label="Home" />
|
||||
<SidebarButton url="/create" logo={<Plus size={18}/>} label="Create Clip" />
|
||||
{user && (
|
||||
<>
|
||||
<SidebarButton url="/my-clips" logo={<Film size={18}/>} label="My Clips" />
|
||||
<SidebarButton url="/profile" logo={<UserIcon size={18}/>} label="Profile" />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</nav>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
export default Sidebar;
|
||||
|
||||
@@ -56,7 +56,7 @@ const Topbar = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx(className, "flex justify-between")}>
|
||||
<div className={clsx(className, "flex justify-between items-center px-4 py-2 bg-white border-b border-gray-200")}>
|
||||
<MenuButton onClick={() => setSidebarToggled(!sidebarToggled)}>
|
||||
{sidebarToggled ? <Menu size={24}/> : <X size={24}/>}
|
||||
</MenuButton>
|
||||
|
||||
@@ -5,7 +5,7 @@ type Props = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
const BlueButton: React.FC<Props> = ({ className = "", ...props }) => {
|
||||
return (
|
||||
<button
|
||||
className={`bg-primary text-text rounded-lg hover:bg-primary-pressed transition-colors duration-100 h-10 ${className}`}
|
||||
className={`bg-primary text-white font-medium rounded-lg hover:bg-primary-pressed transition-colors duration-150 h-10 px-4 ${className}`}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ type Props = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
const MenuButton: React.FC<Props> = ({ className = "", ...props }) => {
|
||||
return (
|
||||
<button
|
||||
className={clsx("p-2 rounded-lg hover:bg-gray-300 hover:text-black transition-colors duration-200", className)}
|
||||
className={clsx("p-2 rounded-md text-gray-600 hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import MenuButton from "./MenuButton.tsx";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
|
||||
type props = {
|
||||
url: string;
|
||||
@@ -8,16 +8,16 @@ type props = {
|
||||
}
|
||||
|
||||
const SidebarButton = ({url, logo, label}: props) => {
|
||||
const { pathname } = useLocation();
|
||||
const isActive = url === "/" ? pathname === "/" : pathname.startsWith(url);
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={"w-full"}
|
||||
to={url}
|
||||
>
|
||||
<MenuButton className={"flex items-center gap-2 w-full"}>
|
||||
<Link className="w-full" to={url}>
|
||||
<MenuButton className={`flex items-center gap-2.5 w-full px-3 py-2 text-sm font-medium rounded-md ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-600"}`}>
|
||||
{logo}{label}
|
||||
</MenuButton>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default SidebarButton;
|
||||
export default SidebarButton;
|
||||
|
||||
@@ -7,6 +7,8 @@ type prop = {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const inputClass = "border border-gray-300 bg-white rounded-md w-full p-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-colors";
|
||||
|
||||
export default function ConfigBox({setMetadata, className}: prop) {
|
||||
const updateRes = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const vals = e.target.value.split(",");
|
||||
@@ -32,15 +34,12 @@ export default function ConfigBox({setMetadata, className}: prop) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx("flex flex-col gap-2 p-10", className)}>
|
||||
<h2 className={"text-xl font-bold"}>Export</h2>
|
||||
<div className={clsx("flex flex-col gap-3 p-6", className)}>
|
||||
<h2 className="text-sm font-semibold text-gray-800 mb-1">Export Settings</h2>
|
||||
|
||||
<Selector label={"Resolution"}>
|
||||
<select id="resolution"
|
||||
name="resolution"
|
||||
defaultValue="1280,720"
|
||||
onChange={updateRes}
|
||||
className={"border-black bg-gray-200 rounded-md w-full p-2"}>
|
||||
<Selector label="Resolution">
|
||||
<select id="resolution" name="resolution" defaultValue="1280,720"
|
||||
onChange={updateRes} className={inputClass}>
|
||||
<option value="3840,2160">2160p</option>
|
||||
<option value="2560,1440">1440p</option>
|
||||
<option value="1920,1080">1080p</option>
|
||||
@@ -50,27 +49,20 @@ export default function ConfigBox({setMetadata, className}: prop) {
|
||||
</select>
|
||||
</Selector>
|
||||
|
||||
<Selector label={"FPS"}>
|
||||
<select id="fps"
|
||||
name="fps"
|
||||
defaultValue="30"
|
||||
onChange={updateFps}
|
||||
className={"border-black bg-gray-200 rounded-md w-full p-2"}>
|
||||
<Selector label="FPS">
|
||||
<select id="fps" name="fps" defaultValue="30"
|
||||
onChange={updateFps} className={inputClass}>
|
||||
<option value="60">60</option>
|
||||
<option value="30">30</option>
|
||||
<option value="15">15</option>
|
||||
</select>
|
||||
</Selector>
|
||||
|
||||
<Selector label={"File Size Limit (MB)"}>
|
||||
<input type="number"
|
||||
min="1"
|
||||
defaultValue="10"
|
||||
onChange={updateFileSize}
|
||||
className={"border-black bg-gray-200 rounded-md w-full p-2"}
|
||||
<Selector label="File Size Limit (MB)">
|
||||
<input type="number" min="1" defaultValue="10"
|
||||
onChange={updateFileSize} className={inputClass}
|
||||
/>
|
||||
</Selector>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,19 @@ const ExportWidget = ({dataSend, handleDownload, downloadable, progress}: props)
|
||||
Export
|
||||
</BlueButton>
|
||||
|
||||
{ downloadable ?
|
||||
(<BlueButton
|
||||
onClick={handleDownload}>
|
||||
Download
|
||||
</BlueButton>)
|
||||
:(
|
||||
<progress
|
||||
value={progress}
|
||||
className={"bg-gray-300 rounded-lg h-1"}>
|
||||
</progress> )
|
||||
}
|
||||
<div className="h-10 flex items-center">
|
||||
{ downloadable ?
|
||||
(<BlueButton className="w-full" onClick={handleDownload}>
|
||||
Download
|
||||
</BlueButton>)
|
||||
:(
|
||||
<progress
|
||||
value={progress}
|
||||
className="w-full h-1.5 rounded bg-gray-200">
|
||||
</progress>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@ type MetadataBoxProps = {
|
||||
|
||||
const MetadataBox = ({setMetadata, className}: MetadataBoxProps) => {
|
||||
return (
|
||||
<div className={clsx("flex flex-col content-between p-10 gap-2", className)}>
|
||||
{/*<h2 className={"text-2xl font-bold col-span-2"}>Metadata</h2>*/}
|
||||
|
||||
<p className={"w-full font-bold text-xl "}>Title</p>
|
||||
<div className={clsx("flex flex-col content-between p-6 gap-2", className)}>
|
||||
<p className="w-full font-semibold text-gray-800 text-sm">Title</p>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter title"
|
||||
@@ -19,10 +17,10 @@ const MetadataBox = ({setMetadata, className}: MetadataBoxProps) => {
|
||||
...prevState,
|
||||
title: e.target.value
|
||||
}))}
|
||||
className={"border-black bg-gray-200 rounded-md w-full p-2"}
|
||||
className="border border-gray-300 bg-white rounded-md w-full p-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-colors"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MetadataBox;
|
||||
export default MetadataBox;
|
||||
|
||||
@@ -77,7 +77,7 @@ export default function Playbar({ video, videoMetadata, className }: Props) {
|
||||
</div>
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
className={"hover:bg-gray-300 rounded-full p-1"}
|
||||
className={"hover:bg-gray-100 rounded-full p-1.5 transition-colors duration-150 text-gray-700"}
|
||||
>
|
||||
{isPlaying ? <Pause size={24} /> : <Play size={24} />}
|
||||
</button>
|
||||
|
||||
@@ -40,41 +40,24 @@ const VideoCard = ({
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<Link to={"/video/" + id}>
|
||||
<div className={clsx("flex flex-col", className)}>
|
||||
<div className={"relative inline-block"}>
|
||||
<div className={clsx("flex flex-col group cursor-pointer", className)}>
|
||||
<div className="relative overflow-hidden rounded-lg">
|
||||
<img
|
||||
src={thumbnailAvailable ? API_URL + `/api/v1/download/thumbnail/${id}` : fallbackThumbnail}
|
||||
alt="Video Thumbnail"
|
||||
className="w-full aspect-video object-cover group-hover:scale-105 transition-transform duration-200"
|
||||
/>
|
||||
|
||||
<p className="
|
||||
absolute
|
||||
top-2
|
||||
left-2
|
||||
bg-black bg-opacity-60
|
||||
text-white
|
||||
px-2
|
||||
py-1
|
||||
rounded
|
||||
pointer-events-none
|
||||
text-sm
|
||||
z-1
|
||||
">
|
||||
<span className="absolute top-1.5 left-1.5 bg-black/70 text-white px-1.5 py-0.5 rounded text-xs font-medium pointer-events-none">
|
||||
{formatTime(duration)}
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div className={"flex flex-col justify-between p-2"}>
|
||||
<p>{title == "" ? "(No Title)" : title}</p>
|
||||
<p
|
||||
className={"text-gray-600 text-sm"}
|
||||
>
|
||||
{timeAgo}
|
||||
<div className="flex flex-col p-2">
|
||||
<p className="font-medium text-gray-900 text-sm leading-snug line-clamp-2">
|
||||
{title === "" ? "(No Title)" : title}
|
||||
</p>
|
||||
<p className="text-gray-500 text-xs mt-0.5">{timeAgo}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
Reference in New Issue
Block a user