This commit is contained in:
Manchik
2026-04-25 04:17:13 +03:00
parent c82866b58a
commit 9b7001407f
23 changed files with 1225 additions and 258 deletions
+93 -12
View File
@@ -1,5 +1,9 @@
import { useState } from "react";
import { useState, useRef } from "react";
import type { AccountCreate } from "@/api/types";
import { api } from "@/api/client";
import { useUiStore } from "@/stores/uiStore";
import { useAccountStore } from "@/stores/accountStore";
import { IconUpload } from "@/components/shared/Icons";
import { useT } from "@/lib/i18n";
interface Props {
@@ -9,19 +13,52 @@ interface Props {
export function AddModal({ onSave, onClose }: Props) {
const t = useT();
const addToast = useUiStore((s) => s.addToast);
const loadAccounts = useAccountStore((s) => s.loadAccounts);
const [login, setLogin] = useState("");
const [password, setPassword] = useState("");
const [email, setEmail] = useState("");
const [emailPassword, setEmailPassword] = useState("");
const [proxy, setProxy] = useState("");
const [notes, setNotes] = useState("");
const [mafile, setMafile] = useState<File | null>(null);
const [saving, setSaving] = useState(false);
const fileRef = useRef<HTMLInputElement>(null);
const handleSave = () => {
const parseLoginPass = (value: string) => {
const sep = value.includes("|") ? "|" : ":";
const parts = value.split(sep);
if (parts.length >= 2) {
setLogin(parts[0]);
setPassword(parts[1]);
if (parts.length >= 3) setEmail(parts[2]);
if (parts.length >= 4) setEmailPassword(parts[3]);
} else {
setLogin(value);
}
};
const handleSave = async () => {
if (!login || !password) return;
onSave({
login,
password,
email: email || undefined,
proxy: proxy || undefined,
});
setSaving(true);
try {
onSave({
login,
password,
email: email || undefined,
email_password: emailPassword || undefined,
proxy: proxy || undefined,
notes: notes || undefined,
});
if (mafile) {
await api.uploadMafiles([mafile]);
}
} catch (e: unknown) {
addToast("error", e instanceof Error ? e.message : String(e));
} finally {
setSaving(false);
}
};
return (
@@ -29,11 +66,55 @@ export function AddModal({ onSave, onClose }: Props) {
<div className="confirm-box max-w-md" onMouseDown={(e) => e.stopPropagation()}>
<h3 className="text-lg font-semibold mb-4">{t("modal.addAccount")}</h3>
<div className="space-y-3">
<input autoFocus value={login} onChange={(e) => setLogin(e.target.value)} placeholder={t("ph.login")} className="w-full bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
<input
autoFocus
value={login}
onChange={(e) => setLogin(e.target.value)}
onPaste={(e) => {
const text = e.clipboardData.getData("text").trim();
if (text.includes(":") || text.includes("|")) {
e.preventDefault();
parseLoginPass(text);
}
}}
placeholder={t("ph.login") + " (login:pass)"}
className="w-full bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm"
/>
<input value={password} onChange={(e) => setPassword(e.target.value)} placeholder={t("ph.password")} className="w-full bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
<input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" className="w-full bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
<input value={proxy} onChange={(e) => setProxy(e.target.value)} placeholder={t("ph.proxy")} className="w-full bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
<button onClick={handleSave} className="btn-primary w-full">{t("btn.add")}</button>
<div className="grid grid-cols-2 gap-2">
<input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" className="bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
<input value={emailPassword} onChange={(e) => setEmailPassword(e.target.value)} placeholder={t("ph.emailPassword")} className="bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
</div>
<div className="grid grid-cols-2 gap-2">
<input value={proxy} onChange={(e) => setProxy(e.target.value)} placeholder={t("ph.proxyOptional")} className="bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
<input value={notes} onChange={(e) => setNotes(e.target.value)} placeholder={t("ph.notesOptional")} className="bg-dark-700 border border-dark-600 rounded px-3 py-2 text-sm" />
</div>
<div
onClick={() => fileRef.current?.click()}
className="border border-dashed border-dark-500 rounded p-3 text-center cursor-pointer transition-colors hover:border-accent flex items-center justify-center gap-2"
>
<IconUpload size={14} className="text-gray-500" />
<span className="text-xs text-gray-400">
{mafile ? mafile.name : t("import.attachMafile")}
</span>
{mafile && (
<button
onClick={(e) => { e.stopPropagation(); setMafile(null); }}
className="text-xs text-red-400 hover:text-red-300 ml-1"
>
</button>
)}
</div>
<input ref={fileRef} type="file" accept=".mafile" className="hidden" onChange={(e) => {
const f = e.target.files?.[0];
if (f) setMafile(f);
}} />
<button onClick={handleSave} disabled={!login || !password || saving} className="btn-primary w-full">
{saving ? t("import.uploading") : t("btn.add")}
</button>
</div>
</div>
</div>