1import { zodResolver } from "@hookform/resolvers/zod";
2import { useFieldArray, useForm } from "react-hook-form";
3import { useTranslation } from "react-i18next";
4import { schema } from "./schema";
5import { RuleItem } from "./rule-item";
6import {
7 HStack,
8 Button,
9 Form,
10 Sortable,
11 ScrollArea,
12 FormLabel,
13 Separator,
14 Awaiting,
15} from "@marzneshin/components";
16import { Schema } from "./schema"
17import { Overlay } from "./overlay"
18import {
19 TemplateOnAcceptanceField,
20 ProfileTitleField,
21 SupportLinkField,
22 UpdateIntervalField,
23 PlaceholderRemarkField,
24 PlaceholderIfDisabledField,
25 ShuffleConfigsField
26} from "./fields";
27import { NoRulesAlert } from "./no-rules-alert";
28import {
29 useSubscriptionSettingsQuery,
30 useSubscriptionSettingsMutation,
31} from "@marzneshin/features/subscription-settings";
32import { useEffect, useCallback } from "react";
33
34export function SubscriptionRulesForm() {
35 const { t } = useTranslation()
36 const { data, isFetching } = useSubscriptionSettingsQuery()
37 const subscriptionSettings = useSubscriptionSettingsMutation()
38 const form = useForm<Schema>({
39 resolver: zodResolver(schema),
40 defaultValues: data,
41 })
42
43 const handleResetLocalChanges = useCallback(
44 () => {
45 form.reset(data)
46 },
47 [form, data],
48 )
49
50 const handleSubscriptionRulesDataUpdate = useCallback(
51 () => {
52 form.reset(data, {
53 keepDirtyValues: true
54 })
55 },
56 [form, data],
57 )
58
59 useEffect(() => {
60 if (data) {
61 handleSubscriptionRulesDataUpdate()
62 }
63 }, [data, handleSubscriptionRulesDataUpdate])
64
65 const onSubmit = (data: Schema) => {
66 subscriptionSettings.mutate(data)
67 }
68
69 const { fields, append, move, remove } = useFieldArray({
70 control: form.control,
71 name: "rules"
72 })
73
74 return (
75 <Form {...form}>
76 <form
77 onSubmit={form.handleSubmit(onSubmit)}
78 className="flex w-full max-w-4xl flex-col gap-2"
79 >
80 <ProfileTitleField />
81 <div className="md:grid grid-cols-2 gap-2 flex flex-col">
82 <UpdateIntervalField />
83 <SupportLinkField />
84 </div>
85 <TemplateOnAcceptanceField />
86 <PlaceholderRemarkField />
87 <PlaceholderIfDisabledField />
88 <ShuffleConfigsField />
89 <Separator className="my-3" />
90 <h4 className="text-lg mt-2">
91 {t("page.settings.subscription-settings.subscription-title")}
92 </h4>
93 <p className="text-sm text-muted-foreground">
94 {t("page.settings.subscription-settings.subscription-desc")}
95 </p>
96 <Awaiting
97 isFetching={isFetching}
98 Skeleton={
99 <>
100 <Overlay />
101 <Overlay />
102 <Overlay />
103 <Overlay />
104 </>
105 }
106 isNotFound={fields.length === 0}
107 NotFound={<NoRulesAlert />}
108 Component={
109 <div className="space-y-1">
110 <Sortable
111 value={fields}
112 onMove={({ activeIndex, overIndex }) =>
113 move(activeIndex, overIndex)
114 }
115 >
116 <div className="grid grid-cols-[2fr,1.3fr,0.25fr,0.25fr] items-center justify-start gap-2 my-2">
117 <FormLabel>
118 {t("pattern")}
119 </FormLabel>
120 <FormLabel>
121 {t("result")}
122 </FormLabel>
123 </div>
124 <ScrollArea className="flex flex-col w-full max-h-[400px] gap-2" type="always">
125 {fields.map((field, index) => (
126 <RuleItem
127 field={field}
128 index={index}
129 onRemove={remove} />
130 ))}
131 </ScrollArea>
132 </Sortable>
133 </div>
134 }
135 />
136 <HStack className="w-full flex-end">
137 <Button
138 type="button"
139 variant="destructive"
140 className="w-fit"
141 onMouseDown={handleResetLocalChanges}
142 >
143 {t("page.settings.subscription-settings.reset-local-changes")}
144 </Button>
145 <Button
146 type="button"
147 variant="outline"
148 className="w-fit"
149 onClick={() =>
150 append({
151 pattern: "",
152 result: "block",
153 })
154 }
155 >
156 {t("page.settings.subscription-settings.add-rule")}
157 </Button>
158 <Button type="submit" className="w-fit">
159 {t("submit")}
160 </Button>
161 </HStack>
162 </form>
163 </Form>
164 )
165}