mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 17:55:33 +00:00
Fix TIDBSubmissionForm.tsx
This commit is contained in:
parent
32b0588530
commit
59dd96e85d
1 changed files with 242 additions and 187 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import classNames from "classnames";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Button } from "@/components/buttons/Button";
|
||||
|
|
@ -11,6 +13,9 @@ import { usePlayerStore } from "@/stores/player/store";
|
|||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
import { submitIntro } from "@/utils/tidb";
|
||||
|
||||
import { IconPatch } from "../buttons/IconPatch";
|
||||
import { Flare } from "../utils/Flare";
|
||||
|
||||
type SegmentType = "intro" | "recap" | "credits" | "preview";
|
||||
|
||||
// Helper function to parse time format (hh:mm:ss, mm:ss, or seconds)
|
||||
|
|
@ -200,195 +205,245 @@ export function TIDBSubmissionForm({
|
|||
|
||||
return (
|
||||
<Modal id={submissionModal.id}>
|
||||
<div className="w-full max-w-[32rem] md:max-w-[50rem] lg:max-w-[60rem] m-4 px-4 max-h-[90vh] overflow-y-auto pointer-events-none">
|
||||
<div className="w-full bg-modal-background rounded-xl p-6 md:p-8 pointer-events-auto">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Icon icon={Icons.CLOCK} className="h-5 w-5 text-white" />
|
||||
<Heading3 className="!mt-0 !mb-0">
|
||||
{t("player.skipTime.feedback.modal.title")}
|
||||
</Heading3>
|
||||
</div>
|
||||
<Paragraph className="!mt-2 !mb-6 text-gray-300">
|
||||
{t("player.skipTime.feedback.modal.description")}
|
||||
</Paragraph>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Section: Segment type and timestamps (example-style card) */}
|
||||
<div className="space-y-4 rounded-xl border border-background-secondary bg-authentication-inputBg/50 p-6">
|
||||
<h2 className="text-lg font-semibold text-white">
|
||||
{t("player.skipTime.feedback.modal.segmentType")}
|
||||
<span className="text-red-500 ml-1">*</span>
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(["intro", "recap", "credits", "preview"] as const).map(
|
||||
(seg) => (
|
||||
<Button
|
||||
key={seg}
|
||||
theme="secondary"
|
||||
className={
|
||||
formData.segment === seg
|
||||
? "!border-2 !border-buttons-purple !bg-buttons-purple/20 focus:outline-none focus-visible:outline-none"
|
||||
: "!border-2 !border-background-secondary hover:!bg-authentication-inputBg focus:outline-none focus-visible:outline-none"
|
||||
}
|
||||
onClick={() => setFormData({ ...formData, segment: seg })}
|
||||
>
|
||||
{seg === "intro"
|
||||
? t("player.skipTime.feedback.modal.types.intro")
|
||||
: seg === "recap"
|
||||
? t("player.skipTime.feedback.modal.types.recap")
|
||||
: seg === "credits"
|
||||
? t("player.skipTime.feedback.modal.types.credits")
|
||||
: t("player.skipTime.feedback.modal.types.preview")}
|
||||
</Button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-400">
|
||||
{t("player.skipTime.feedback.modal.whenToDesc")}
|
||||
</p>
|
||||
|
||||
<form ref={formRef} onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="tidb-start"
|
||||
className="block text-sm font-medium text-white mb-1"
|
||||
>
|
||||
{t("player.skipTime.feedback.modal.startTimeLabel")}
|
||||
{formData.segment === "credits" ||
|
||||
formData.segment === "preview" ? (
|
||||
<span className="text-red-500 ml-1">*</span>
|
||||
) : null}
|
||||
</label>
|
||||
<AuthInputBox
|
||||
value={formData.start}
|
||||
onChange={(value) =>
|
||||
setFormData({ ...formData, start: value })
|
||||
}
|
||||
placeholder={t(
|
||||
`player.skipTime.feedback.modal.placeholders.start.${formData.segment}`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="tidb-end"
|
||||
className="block text-sm font-medium text-white mb-1"
|
||||
>
|
||||
{t("player.skipTime.feedback.modal.endTimeLabel")}
|
||||
{formData.segment === "intro" ||
|
||||
formData.segment === "recap" ? (
|
||||
<span className="text-red-500 ml-1">*</span>
|
||||
) : null}
|
||||
</label>
|
||||
<AuthInputBox
|
||||
value={formData.end}
|
||||
onChange={(value) =>
|
||||
setFormData({ ...formData, end: value })
|
||||
}
|
||||
placeholder={t(
|
||||
`player.skipTime.feedback.modal.placeholders.end.${formData.segment}`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timing guidance (segment-specific) */}
|
||||
<div className="rounded-lg border border-background-secondary bg-modal-background p-4">
|
||||
<h3 className="font-semibold text-white mb-2 text-sm">
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.whenToTitle`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.whenToTitle",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-3 text-xs text-gray-400">
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t("player.skipTime.feedback.modal.guide.startLabel")}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.startDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.startDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t("player.skipTime.feedback.modal.guide.endLabel")}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.endDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.endDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t(
|
||||
"player.skipTime.feedback.modal.guide.durationLabel",
|
||||
)}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.durationDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.durationDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t("player.skipTime.feedback.modal.guide.excludeLabel")}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.excludeDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.excludeDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 pt-2 justify-between">
|
||||
<Button
|
||||
theme="secondary"
|
||||
onClick={handleClose}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{t("player.skipTime.feedback.modal.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
theme="purple"
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
icon={Icons.ARROW_RIGHT}
|
||||
onClick={() => formRef.current?.requestSubmit()}
|
||||
>
|
||||
{isSubmitting
|
||||
? t("player.skipTime.feedback.modal.submitting")
|
||||
: t("player.skipTime.feedback.modal.submit")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<Helmet>
|
||||
<html data-no-scroll />
|
||||
</Helmet>
|
||||
<div className="flex absolute inset-0 items-center justify-center pt-safe">
|
||||
<Flare.Base
|
||||
className={classNames(
|
||||
"group -m-[0.705em] rounded-3xl bg-background-main",
|
||||
"max-w-[1200px] max-h-[650px]",
|
||||
"bg-mediaCard-hoverBackground/60 backdrop-filter backdrop-blur-lg shadow-lg overflow-hidden",
|
||||
"h-[97%] w-[95%]",
|
||||
"relative",
|
||||
)}
|
||||
>
|
||||
<div className="transition-transform duration-300 h-full relative">
|
||||
<Flare.Light
|
||||
flareSize={300}
|
||||
cssColorVar="--colors-mediaCard-hoverAccent"
|
||||
backgroundClass="bg-modal-background duration-100"
|
||||
className="rounded-3xl bg-background-main group-hover:opacity-100 transition-opacity duration-300"
|
||||
/>
|
||||
<div className="absolute right-4 top-4 z-50 pointer-events-auto">
|
||||
<button
|
||||
type="button"
|
||||
className="text-s font-semibold text-type-secondary hover:text-white transition-transform hover:scale-95 select-none"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<IconPatch icon={Icons.X} />
|
||||
</button>
|
||||
</div>
|
||||
<Flare.Child className="pointer-events-auto relative h-full overflow-y-auto scrollbar-none select-text p-6 pt-20 sm:pt-6">
|
||||
<div className="select-text">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Icon icon={Icons.CLOCK} className="h-5 w-5 text-white" />
|
||||
<Heading3 className="!mt-0 !mb-0">
|
||||
{t("player.skipTime.feedback.modal.title")}
|
||||
</Heading3>
|
||||
</div>
|
||||
<Paragraph className="!mt-2 !mb-6 text-gray-300">
|
||||
{t("player.skipTime.feedback.modal.description")}
|
||||
</Paragraph>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Section: Segment type and timestamps (example-style card) */}
|
||||
<div className="space-y-4 rounded-xl border border-background-secondary bg-authentication-inputBg/50 p-6">
|
||||
<h2 className="text-lg font-semibold text-white">
|
||||
{t("player.skipTime.feedback.modal.segmentType")}
|
||||
<span className="text-red-500 ml-1">*</span>
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(["intro", "recap", "credits", "preview"] as const).map(
|
||||
(seg) => (
|
||||
<Button
|
||||
key={seg}
|
||||
theme="secondary"
|
||||
className={
|
||||
formData.segment === seg
|
||||
? "!border-2 !border-buttons-purple !bg-buttons-purple/20 focus:outline-none focus-visible:outline-none"
|
||||
: "!border-2 !border-background-secondary hover:!bg-authentication-inputBg focus:outline-none focus-visible:outline-none"
|
||||
}
|
||||
onClick={() =>
|
||||
setFormData({ ...formData, segment: seg })
|
||||
}
|
||||
>
|
||||
{seg === "intro"
|
||||
? t("player.skipTime.feedback.modal.types.intro")
|
||||
: seg === "recap"
|
||||
? t(
|
||||
"player.skipTime.feedback.modal.types.recap",
|
||||
)
|
||||
: seg === "credits"
|
||||
? t(
|
||||
"player.skipTime.feedback.modal.types.credits",
|
||||
)
|
||||
: t(
|
||||
"player.skipTime.feedback.modal.types.preview",
|
||||
)}
|
||||
</Button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-400">
|
||||
{t("player.skipTime.feedback.modal.whenToDesc")}
|
||||
</p>
|
||||
|
||||
<form
|
||||
ref={formRef}
|
||||
onSubmit={handleSubmit}
|
||||
className="space-y-4"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="tidb-start"
|
||||
className="block text-sm font-medium text-white mb-1"
|
||||
>
|
||||
{t("player.skipTime.feedback.modal.startTimeLabel")}
|
||||
{formData.segment === "credits" ||
|
||||
formData.segment === "preview" ? (
|
||||
<span className="text-red-500 ml-1">*</span>
|
||||
) : null}
|
||||
</label>
|
||||
<AuthInputBox
|
||||
value={formData.start}
|
||||
onChange={(value) =>
|
||||
setFormData({ ...formData, start: value })
|
||||
}
|
||||
placeholder={t(
|
||||
`player.skipTime.feedback.modal.placeholders.start.${formData.segment}`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="tidb-end"
|
||||
className="block text-sm font-medium text-white mb-1"
|
||||
>
|
||||
{t("player.skipTime.feedback.modal.endTimeLabel")}
|
||||
{formData.segment === "intro" ||
|
||||
formData.segment === "recap" ? (
|
||||
<span className="text-red-500 ml-1">*</span>
|
||||
) : null}
|
||||
</label>
|
||||
<AuthInputBox
|
||||
value={formData.end}
|
||||
onChange={(value) =>
|
||||
setFormData({ ...formData, end: value })
|
||||
}
|
||||
placeholder={t(
|
||||
`player.skipTime.feedback.modal.placeholders.end.${formData.segment}`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timing guidance (segment-specific) */}
|
||||
<div className="rounded-lg border border-background-secondary bg-modal-background p-4">
|
||||
<h3 className="font-semibold text-white mb-2 text-sm">
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.whenToTitle`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.whenToTitle",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-3 text-xs text-gray-400">
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t(
|
||||
"player.skipTime.feedback.modal.guide.startLabel",
|
||||
)}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.startDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.startDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t(
|
||||
"player.skipTime.feedback.modal.guide.endLabel",
|
||||
)}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.endDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.endDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t(
|
||||
"player.skipTime.feedback.modal.guide.durationLabel",
|
||||
)}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.durationDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.durationDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium text-gray-300 block mb-0.5">
|
||||
{t(
|
||||
"player.skipTime.feedback.modal.guide.excludeLabel",
|
||||
)}
|
||||
</span>
|
||||
{t(
|
||||
`player.skipTime.feedback.modal.guide.${formData.segment}.excludeDesc`,
|
||||
{
|
||||
defaultValue: t(
|
||||
"player.skipTime.feedback.modal.guide.excludeDesc",
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 pt-2 justify-between">
|
||||
<Button
|
||||
theme="secondary"
|
||||
onClick={handleClose}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{t("player.skipTime.feedback.modal.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
theme="purple"
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
icon={Icons.ARROW_RIGHT}
|
||||
onClick={() => formRef.current?.requestSubmit()}
|
||||
>
|
||||
{isSubmitting
|
||||
? t("player.skipTime.feedback.modal.submitting")
|
||||
: t("player.skipTime.feedback.modal.submit")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Flare.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Flare.Base>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue