feat: darkmode draft version
This commit is contained in:
@@ -40,7 +40,7 @@ export default function APIConfig() {
|
||||
data-checked={thirdParty}
|
||||
/>
|
||||
<div className="w-full flex flex-col items-start gap-2">
|
||||
<label htmlFor="secret" className="text-sm text-gray-500"> {t("third_app.key")}:</label>
|
||||
<label htmlFor="secret" className="text-sm text-gray-500 dark:text-gray-100"> {t("third_app.key")}:</label>
|
||||
<Input disabled={!thirdParty} type="password" id="secret" value={updatedSecret || data} />
|
||||
</div>
|
||||
<Tippy
|
||||
|
||||
@@ -14,11 +14,11 @@ const APIDocument = () => {
|
||||
};
|
||||
return (
|
||||
<section className="flex flex-col justify-start items-start gap-4">
|
||||
<div className="text-gray-500 ">
|
||||
<div className="text-gray-500 dark:text-gray-100">
|
||||
{t("api_doc.desc")}
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<h2 className='text-gray-700 text-xl font-bold'>
|
||||
<h2 className='text-gray-700 dark:text-white text-xl font-bold'>
|
||||
{t("api_doc.access")}
|
||||
</h2>
|
||||
<a href={APIUrl} target="_blank" rel="noopener noreferrer" className='underline text-primary-600'>
|
||||
@@ -26,12 +26,12 @@ const APIDocument = () => {
|
||||
</a>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<h2 className='text-gray-700 text-xl font-bold'>
|
||||
<h2 className='text-gray-700 dark:text-white text-xl font-bold'>
|
||||
{t("api_doc.use_method")}
|
||||
</h2>
|
||||
<div className=" flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className='text-gray-700 text-lg'>
|
||||
<h3 className='text-gray-700 dark:text-white text-lg'>
|
||||
👉 {t("api_doc.step_1")}
|
||||
</h3>
|
||||
{/* <div className="flex flex-col gap-1"> */}
|
||||
@@ -39,8 +39,8 @@ const APIDocument = () => {
|
||||
{/* </div> */}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className='text-gray-700 text-lg flex items-center gap-2'>
|
||||
👉 {t("api_doc.step_2")} <span className='text-gray-500 text-sm'>
|
||||
<h3 className='text-gray-700 dark:text-white text-lg flex items-center gap-2'>
|
||||
👉 {t("api_doc.step_2")} <span className='text-gray-500 dark:text-white text-sm'>
|
||||
({t("api_doc.step_2_desc")})
|
||||
</span>
|
||||
</h3>
|
||||
@@ -53,7 +53,7 @@ const APIDocument = () => {
|
||||
<img className='border border-solid rounded-md border-gray-300 shadow-lg w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step2.jpg" alt="step 2" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className='text-gray-700 text-lg flex items-center gap-2'>
|
||||
<h3 className='text-gray-700 dark:text-white text-lg flex items-center gap-2'>
|
||||
👉 {t("api_doc.last")}
|
||||
</h3>
|
||||
<img className='border border-solid rounded-md border-gray-300 shadow-lg w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step3.png" alt="step 3" />
|
||||
|
||||
@@ -21,7 +21,7 @@ type Props = {
|
||||
// </table>;
|
||||
// };
|
||||
type DeleteAPIKeyProps = { uid: number, kid: number }
|
||||
const tdClass = "p-1 whitespace-nowrap text-xs text-gray-500 align-middle px-1";
|
||||
const tdClass = "p-1 whitespace-nowrap text-xs text-gray-500 dark:text-gray-200 align-middle px-1";
|
||||
const BotAPIKeys = ({ uid }: Props) => {
|
||||
const { t } = useTranslation("setting", { keyPrefix: "bot" });
|
||||
const [currentUid, setCurrentUid] = useState<number | undefined>();
|
||||
@@ -39,11 +39,11 @@ const BotAPIKeys = ({ uid }: Props) => {
|
||||
const colWidths = ["w-20", "w-[166px]", "w-36", "w-15", "w-10"];
|
||||
return (
|
||||
<div className='flex flex-col gap-2 items-start'>
|
||||
<div className='border-t border-solid border-b border-gray-100 py-2 w-full'>
|
||||
<div className='border-t border-solid border-b border-gray-100 dark:border-gray-500 py-2 w-full'>
|
||||
<table className="min-w-full table-fixed font-mono">
|
||||
<thead >
|
||||
<tr >
|
||||
{[t("col_key_name"), t('col_key_value'), t('col_key_create_time'), t('col_key_last_used'), ""].map((title, idx) => <th key={title} scope="col" className={clsx(`text-xs text-gray-900 px-1 text-left pb-2`, colWidths[idx])}>
|
||||
{[t("col_key_name"), t('col_key_value'), t('col_key_create_time'), t('col_key_last_used'), ""].map((title, idx) => <th key={title} scope="col" className={clsx(`text-xs text-gray-900 dark:text-gray-50 px-1 text-left pb-2`, colWidths[idx])}>
|
||||
{title}
|
||||
</th>)}
|
||||
</tr>
|
||||
|
||||
@@ -77,12 +77,12 @@ const CreateModal = ({ closeModal }: Props) => {
|
||||
</>
|
||||
}
|
||||
>
|
||||
<form ref={formRef} className="w-full flex flex-col gap-2 items-center" action="/">
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<form ref={formRef} className="w-full flex flex-col gap-2" action="/">
|
||||
<div className="flex flex-col items-start gap-1 w-full">
|
||||
<label htmlFor={"name"} className="text-sm text-[#6b7280]">Name</label>
|
||||
<Input name={"name"} required placeholder='Please input bot name'></Input>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<div className="flex flex-col items-start gap-1 w-full">
|
||||
<label htmlFor={"webhook_url"} className="text-sm text-[#6b7280]">Webhook URL (Optional)</label>
|
||||
<Input name={"webhook_url"} type="url" placeholder='Please input webhook url'></Input>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ const WebhookEdit = ({ uid }: Props) => {
|
||||
evt.preventDefault();
|
||||
handleEdit();
|
||||
}}>
|
||||
<input readOnly={!editable} required autoFocus type="url" name='webhook' defaultValue={url} className={clsx("text-sm text-gray-500 px-2 py-1", editable ? "border border-solid border-gray-200 bg-gray-50" : "bg-transparent")} />
|
||||
<input readOnly={!editable} required autoFocus type="url" name='webhook' defaultValue={url} className={clsx("text-sm text-gray-500 dark:text-gray-100 px-2 py-1", editable ? "border border-solid border-gray-200 bg-gray-50" : "bg-transparent")} />
|
||||
</form>
|
||||
<button type='button' disabled={isUpdating} onClick={handleEdit}>
|
||||
{isUpdating ? <Orbit size={16} /> : editable ?
|
||||
|
||||
@@ -20,11 +20,11 @@ type TipProps = { title: string, desc: string };
|
||||
const Tip = ({ title, desc }: TipProps) => {
|
||||
|
||||
return <div className="flex flex-col text-sm">
|
||||
<h2 className="">{title}</h2>
|
||||
<p className="text-gray-500">{desc}</p>
|
||||
<h2 className="dark:text-white">{title}</h2>
|
||||
<p className="text-gray-500 dark:text-gray-400">{desc}</p>
|
||||
</div>;
|
||||
};
|
||||
const tdClass = "p-6 whitespace-nowrap text-sm font-medium text-gray-900 align-top";
|
||||
const tdClass = "p-6 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100 align-top";
|
||||
type WebhookParams = { webhook?: string, uid: number };
|
||||
type DeleteParams = { name: string, uid: number };
|
||||
export default function BotConfig() {
|
||||
@@ -65,19 +65,19 @@ export default function BotConfig() {
|
||||
<Tip title={t("webhook_tip_title")} desc={t("webhook_tip_desc")} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h2 className='font-semibold flex gap-4 items-center'>
|
||||
<h2 className='font-semibold flex gap-4 items-center dark:text-white'>
|
||||
{t("manage")}
|
||||
<a href="https://doc.voce.chat/bot-and-webhook" target="_blank" className="text-sm text-blue-400 underline-offset-1 underline" rel="noopener noreferrer">
|
||||
🔗 {t("how_to_use")}
|
||||
</a>
|
||||
</h2>
|
||||
<p className='text-gray-500 text-xs'>{t("manage_desc")}</p>
|
||||
<p className='text-gray-500 dark:text-gray-400 text-xs'>{t("manage_desc")}</p>
|
||||
</div>
|
||||
<div className="w-fit overflow-hidden">
|
||||
<table className="min-w-full table-auto">
|
||||
<thead className="border-b bg-gray-50">
|
||||
<thead className="border-b dark:border-b-gray-500 bg-gray-50 dark:bg-gray-600">
|
||||
<tr>
|
||||
{[t("col_avatar"), t('col_name'), t('col_api_key'), t('col_webhook'), t('col_opt')].map(title => <th key={title} scope="col" className="text-sm font-bold text-gray-900 px-6 py-4 text-left">
|
||||
{[t("col_avatar"), t('col_name'), t('col_api_key'), t('col_webhook'), t('col_opt')].map(title => <th key={title} scope="col" className="text-sm font-bold text-gray-900 dark:text-gray-100 px-6 py-4 text-left">
|
||||
{title}
|
||||
</th>)}
|
||||
</tr>
|
||||
@@ -85,10 +85,9 @@ export default function BotConfig() {
|
||||
<tbody>
|
||||
{bots.map(bot => {
|
||||
const { uid, name, avatar } = bot;
|
||||
return <tr key={uid} className="bg-white border-b transition duration-300 ease-in-out hover:bg-gray-100">
|
||||
return <tr key={uid} className="bg-white dark:bg-gray-800 border-b dark:border-b-gray-500 transition duration-300 ease-in-out hover:bg-gray-100">
|
||||
<td className="px-4 py-2">
|
||||
<AvatarUploader uid={uid} url={avatar} uploadImage={updateAvatar} name={name} size={56} />
|
||||
|
||||
</td>
|
||||
<td className={`${tdClass}`}>
|
||||
<div>
|
||||
|
||||
@@ -13,7 +13,7 @@ const Item = ({ label, data, foldable = false, ...rest }: ItemProps) => {
|
||||
const infoClass = clsx("font-bold w-full cursor-pointer", foldable ? " overflow-hidden text-ellipsis" : "whitespace-pre-wrap break-all");
|
||||
if (!data) return null;
|
||||
return <div className="whitespace-nowrap flex flex-col items-start justify-start text-lg">
|
||||
<span className="text-sm text-gray-400">{label}</span>
|
||||
<span className="text-sm text-gray-400 dark:text-white">{label}</span>
|
||||
{Array.isArray(data) ? <ul className={infoClass}>
|
||||
{data.map((d) => {
|
||||
return <li key={d}>{d}</li>;
|
||||
|
||||
@@ -61,16 +61,16 @@ export default function MyAccount() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-start gap-8">
|
||||
<div className="p-6 flex flex-col items-center w-[512px] bg-slate-100 rounded-2xl">
|
||||
<div className="p-6 flex flex-col items-center w-[512px] bg-slate-100 dark:bg-gray-800 rounded-2xl">
|
||||
<AvatarUploader url={avatar} name={name} uploadImage={uploadAvatar} />
|
||||
<div className="mt-2 mb-16 font-bold text-lg text-gray-800">
|
||||
<div className="mt-2 mb-16 font-bold text-lg text-gray-800 dark:text-white">
|
||||
{name} <span className="font-normal text-gray-500">#{uid}</span>
|
||||
</div>
|
||||
<div className="w-full flex justify-between mb-6">
|
||||
<div className="flex flex-col text-gray-500">
|
||||
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
||||
<span className="text-xs uppercase font-semibold">{t("username")}</span>
|
||||
<span className="text-sm ">
|
||||
{name} <span className="text-gray-600"> #{uid}</span>
|
||||
{name} <span className="text-gray-600 dark:text-gray-400"> #{uid}</span>
|
||||
</span>
|
||||
</div>
|
||||
<Button data-edit="name" onClick={handleBasicEdit} className="">
|
||||
@@ -78,7 +78,7 @@ export default function MyAccount() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full flex justify-between mb-6">
|
||||
<div className="flex flex-col text-gray-500">
|
||||
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
||||
<span className="text-xs uppercase font-semibold">{t("email")}</span>
|
||||
<span className="text-sm">{email}</span>
|
||||
</div>
|
||||
@@ -87,7 +87,7 @@ export default function MyAccount() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full flex justify-between mb-6">
|
||||
<div className="flex flex-col text-gray-500">
|
||||
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
||||
<span className="text-xs uppercase font-semibold">{t("password")}</span>
|
||||
<span className="text-sm">*********</span>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Radio from '../../../common/component/styled/Radio';
|
||||
import { Theme } from '../../../types/common';
|
||||
|
||||
// type Props = {}
|
||||
|
||||
const DarkMode = () => {
|
||||
const [theme, setTheme] = useState<Theme>(localStorage.theme || "auto");
|
||||
const { t } = useTranslation("setting");
|
||||
const handleThemeToggle = (v: Theme) => {
|
||||
setTheme(v);
|
||||
localStorage.theme = v;
|
||||
// reset
|
||||
document.documentElement.classList.remove("dark");
|
||||
document.documentElement.classList.remove("light");
|
||||
if (v !== "auto") {
|
||||
document.documentElement.classList.add(v);
|
||||
} else {
|
||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
document.documentElement.classList.add(isDark ? "dark" : 'light');
|
||||
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="text-sm">
|
||||
<p className="dark:text-gray-100 font-semibold">{t("overview.theme.title")}</p>
|
||||
<p className="flex justify-between w-full text-gray-400 mb-2">
|
||||
{t("overview.theme.desc")}
|
||||
</p>
|
||||
<Radio
|
||||
options={[t("overview.theme.auto"), t("overview.theme.dark"), t("overview.theme.light")]}
|
||||
values={['auto', 'dark', 'light']}
|
||||
value={theme}
|
||||
onChange={(v) => {
|
||||
handleThemeToggle(v);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DarkMode;
|
||||
@@ -33,12 +33,11 @@ const Index = () => {
|
||||
// if(!fetch)
|
||||
return (
|
||||
<div className="text-sm">
|
||||
<p className="font-semibold">{t("overview.url.title")}</p>
|
||||
<p className="dark:text-gray-100 font-semibold">{t("overview.url.title")}</p>
|
||||
<p className="flex justify-between w-full text-gray-400">
|
||||
{t("overview.url.desc")}
|
||||
</p>
|
||||
<div className="flex items-center gap-4 mt-2">
|
||||
|
||||
<StyledInput placeholder='frontend url' value={url} onChange={handleChange} />
|
||||
<StyledButton disabled={!url || isLoading} className='' onClick={handleUpdate}> {ct("action.update")}</StyledButton>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import useConfig from "../../../common/hook/useConfig";
|
||||
import Server from './server';
|
||||
import Language from './Language';
|
||||
import FrontendURL from "./FrontendURL";
|
||||
// import DarkMode from "./DarkMode";
|
||||
import ServerVersionChecker from "../../../common/component/ServerVersionChecker";
|
||||
|
||||
export default function Overview() {
|
||||
@@ -33,7 +34,7 @@ export default function Overview() {
|
||||
{isAdmin && (
|
||||
<>
|
||||
<div className="text-sm">
|
||||
<p className="font-semibold">{t("overview.sign_up.title")}</p>
|
||||
<p className="dark:text-gray-100 font-semibold">{t("overview.sign_up.title")}</p>
|
||||
<p className="flex justify-between w-full text-gray-400 mb-2">{t("overview.sign_up.desc")}</p>
|
||||
<StyledRadio
|
||||
options={[t("overview.sign_up.everyone"), t("overview.sign_up.invite")]}
|
||||
@@ -45,7 +46,7 @@ export default function Overview() {
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
<p className="font-semibold">{t("overview.guest_mode.title")}</p>
|
||||
<p className="dark:text-gray-100 font-semibold">{t("overview.guest_mode.title")}</p>
|
||||
<p className="flex justify-between w-full text-gray-400 mb-2">
|
||||
<span className="txt">
|
||||
{t("overview.guest_mode.desc")}
|
||||
@@ -66,6 +67,7 @@ export default function Overview() {
|
||||
</>
|
||||
)}
|
||||
<Language />
|
||||
{/* <DarkMode /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const Index = () => {
|
||||
};
|
||||
return (
|
||||
<div className="text-sm">
|
||||
<p className="font-semibold">{t("overview.lang.title")}</p>
|
||||
<p className="dark:text-gray-100 font-semibold">{t("overview.lang.title")}</p>
|
||||
<p className="flex justify-between w-full text-gray-400 mb-2">
|
||||
{t("overview.lang.desc")}
|
||||
</p>
|
||||
|
||||
@@ -72,7 +72,7 @@ const Index = () => {
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<div className="flex flex-col justify-between items-start">
|
||||
<div className="text-sm text-gray-600">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-100">
|
||||
{t("overview.upload_desc")}
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,7 +80,7 @@ const Index = () => {
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-6 mb-16">
|
||||
<div className="w-full flex flex-col items-start gap-2">
|
||||
<Label htmlFor="name">{t("overview.name")}</Label>
|
||||
<Label className='dark:text-gray-100' htmlFor="name">{t("overview.name")}</Label>
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
data-type="name"
|
||||
@@ -92,7 +92,7 @@ const Index = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex flex-col items-start gap-2">
|
||||
<Label htmlFor="desc">{t("overview.desc")}</Label>
|
||||
<Label className='dark:text-gray-100' htmlFor="desc">{t("overview.desc")}</Label>
|
||||
<Textarea
|
||||
disabled={!isAdmin}
|
||||
data-type="description"
|
||||
|
||||
@@ -5,14 +5,14 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
|
||||
const Row = ({ paramKey, paramDefault, remarks }: { paramKey: string, paramDefault: string | number, remarks: string }) => {
|
||||
return <tr className="bg-white border-b transition duration-300 ease-in-out hover:bg-gray-100">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
return <tr className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-50 border-b transition duration-300 ease-in-out hover:bg-gray-100">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
{paramKey}
|
||||
</td>
|
||||
<td className="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap">
|
||||
<td className="text-sm font-light px-6 py-4 whitespace-nowrap">
|
||||
{paramDefault}
|
||||
</td>
|
||||
<td className="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap">
|
||||
<td className="text-sm font-light px-6 py-4 whitespace-nowrap">
|
||||
{remarks}
|
||||
</td>
|
||||
</tr>;
|
||||
@@ -22,23 +22,23 @@ export default function Widget() {
|
||||
// const disableBtn = !reachLimit;
|
||||
return (
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<div className="text-gray-600 ">
|
||||
<div className="text-gray-600 dark:text-gray-100">
|
||||
{t('tip')}
|
||||
</div>
|
||||
<label htmlFor="code" className="text-gray-500 text-sm mt-5">
|
||||
<label htmlFor="code" className="text-gray-500 dark:text-white text-sm mt-5">
|
||||
{t('code')}:
|
||||
</label>
|
||||
<SyntaxHighlighter id="code" language="html" style={vscDarkPlus} className="rounded">
|
||||
{`<!-- ${t('code_comment')} -->\n<script \n data-host-id="1" \n data-close-width="48" \n data-close-height="48" \n data-open-width="380" \n data-open-height="680" \n src="${location.origin}/widget.js" \n async \n/>`}
|
||||
</SyntaxHighlighter>
|
||||
<div className="text-gray-500 text-sm mt-5 mb-2">
|
||||
<div className="text-gray-500 dark:text-white text-sm mt-5 mb-2">
|
||||
{t('config')}:
|
||||
</div>
|
||||
<div className="w-[700px] border border-solid border-gray-300 rounded overflow-hidden">
|
||||
<div className="w-[700px] border border-solid border-gray-300 dark:border-gray-400 rounded overflow-hidden">
|
||||
<table className="min-w-full table-auto">
|
||||
<thead className="border-b bg-gray-50">
|
||||
<thead className="border-b bg-gray-50 dark:bg-gray-500">
|
||||
<tr>
|
||||
{[t('param_key'), t('default_value'), t('remark')].map(title => <th key={title} scope="col" className="text-sm font-bold text-gray-900 px-6 py-4 text-left">
|
||||
{[t('param_key'), t('default_value'), t('remark')].map(title => <th key={title} scope="col" className="text-sm font-bold text-gray-900 dark:text-white px-6 py-4 text-left">
|
||||
{title}
|
||||
</th>)}
|
||||
</tr>
|
||||
@@ -67,9 +67,9 @@ export default function Widget() {
|
||||
}
|
||||
].map(row => <Row key={row.paramKey} {...row} />)}
|
||||
</tbody>
|
||||
<tfoot className="border-t border-solid border-gray-200" >
|
||||
<tfoot className="border-t border-solid border-gray-200 dark:border-gray-50 dark:bg-gray-500" >
|
||||
<tr>
|
||||
<td colSpan={3} className="text-gray-400 px-5 py-3 text-sm">
|
||||
<td colSpan={3} className="text-gray-400 dark:text-white px-5 py-3 text-sm">
|
||||
* All the parameters are optional, and prefixed by <i className="bg-gray-700 text-white px-1">data-</i>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function ConfigFirebase() {
|
||||
/>
|
||||
{<fieldset className="inputs" disabled={select !== "custom"}>
|
||||
<div className="input">
|
||||
<Label htmlFor="name">{t("firebase.token_url")}</Label>
|
||||
<Label htmlFor="name" className="dark:text-gray-200">{t("firebase.token_url")}</Label>
|
||||
<Input
|
||||
data-type="token_url"
|
||||
onChange={handleChange}
|
||||
@@ -97,7 +97,7 @@ export default function ConfigFirebase() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("firebase.project_id")}</Label>
|
||||
<Label htmlFor="desc" className="dark:text-gray-200">{t("firebase.project_id")}</Label>
|
||||
<Input
|
||||
data-type="project_id"
|
||||
onChange={handleChange}
|
||||
@@ -107,7 +107,7 @@ export default function ConfigFirebase() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("firebase.private_key")}</Label>
|
||||
<Label htmlFor="desc" className="dark:text-gray-200">{t("firebase.private_key")}</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
spellCheck={false}
|
||||
@@ -119,7 +119,7 @@ export default function ConfigFirebase() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("firebase.client_email")}</Label>
|
||||
<Label htmlFor="desc" className="dark:text-gray-200">{t("firebase.client_email")}</Label>
|
||||
<Input
|
||||
data-type="client_email"
|
||||
onChange={handleChange}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ChangeEvent, FC, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Select, { Option } from "../../../../common/component/styled/Select";
|
||||
import Button from "../../../../common/component/styled/Button";
|
||||
import Input from "../../../../common/component/styled/Input";
|
||||
import Toggle from "../../../../common/component/styled/Toggle";
|
||||
import options from "./items.json";
|
||||
import Styled from "./styled";
|
||||
import IconMinus from "../../../../assets/icons/minus.circle.svg";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface Issuer {
|
||||
domain: string;
|
||||
@@ -32,32 +31,31 @@ const IssuerList: FC<Props> = ({ issuers = [], onChange }) => {
|
||||
(!newDomain && !select?.value) ||
|
||||
!select?.title ||
|
||||
issuers.some((issuer) => issuer.domain === newDomain);
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<ul className="issuers">
|
||||
<div className="py-4 w-full flex flex-col gap-4">
|
||||
<ul className="flex flex-col gap-4">
|
||||
{issuers.map(({ enable, favicon, domain }) => {
|
||||
return (
|
||||
<li key={domain} className="issuer">
|
||||
<div className="left">
|
||||
<li key={domain} className="flex items-center justify-between gap-10">
|
||||
<div className="flex-1 flex items-center justify-between">
|
||||
<IconMinus
|
||||
className="remove"
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
onChange(issuers.filter((issuer) => issuer.domain !== domain));
|
||||
}}
|
||||
/>
|
||||
<div className="data">
|
||||
{Boolean(favicon) && <img src={favicon} alt="logo" className="icon" />}
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
{Boolean(favicon) && <img src={favicon} alt="logo" className="w-8 h-8" />}
|
||||
<Input
|
||||
readOnly
|
||||
value={domain}
|
||||
prefix="https://"
|
||||
placeholder="Issuer Domain"
|
||||
className="url"
|
||||
className="w-[280px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="right">
|
||||
<div className="w-14 flex justify-end">
|
||||
<Toggle
|
||||
data-checked={enable}
|
||||
onClick={() => {
|
||||
@@ -74,8 +72,8 @@ const IssuerList: FC<Props> = ({ issuers = [], onChange }) => {
|
||||
);
|
||||
})}
|
||||
|
||||
<li className="issuer add">
|
||||
<div className="left">
|
||||
<li className="flex items-center justify-between gap-10 cursor-pointer">
|
||||
<div className="flex-1 flex items-center justify-between">
|
||||
<Select
|
||||
options={options.map((option) => ({
|
||||
...option,
|
||||
@@ -84,18 +82,18 @@ const IssuerList: FC<Props> = ({ issuers = [], onChange }) => {
|
||||
current={select}
|
||||
updateSelect={setSelect}
|
||||
/>
|
||||
<div className="data">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<Input
|
||||
onChange={handleNewDomain}
|
||||
readOnly={!!select?.value}
|
||||
value={select?.value || newDomain}
|
||||
prefix="https://"
|
||||
placeholder="domain.com"
|
||||
className="url"
|
||||
className="w-[280px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="right">
|
||||
<div className="w-14 flex justify-end">
|
||||
<Button
|
||||
disabled={disableBtn}
|
||||
onClick={() => {
|
||||
@@ -119,7 +117,7 @@ const IssuerList: FC<Props> = ({ issuers = [], onChange }) => {
|
||||
</li>
|
||||
</ul>
|
||||
{/* <IconPlus className="add" /> */}
|
||||
</Styled>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 16px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
.issuers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
.issuer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 40px;
|
||||
.left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.remove {
|
||||
cursor: pointer;
|
||||
}
|
||||
.data {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
justify-content: space-between;
|
||||
> .icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
> .url {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 56px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
.add {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Styled;
|
||||
@@ -77,9 +77,9 @@ export default function Logins() {
|
||||
<div className="row">
|
||||
<div className="title">
|
||||
<div className="txt">
|
||||
<Label>{t("password")}</Label>
|
||||
<Label className="dark:text-gray-200">{t("password")}</Label>
|
||||
</div>
|
||||
<span className="desc">{t("password_desc")}</span>
|
||||
<span className="desc dark:!text-gray-400" >{t("password_desc")}</span>
|
||||
</div>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { password: !password })}
|
||||
@@ -91,9 +91,9 @@ export default function Logins() {
|
||||
<div className="row">
|
||||
<div className="title">
|
||||
<div className="txt">
|
||||
<Label>{t("magic_link")}</Label>
|
||||
<Label className="dark:text-gray-200">{t("magic_link")}</Label>
|
||||
</div>
|
||||
<span className="desc">{t("magic_link_desc")}</span>
|
||||
<span className="desc dark:!text-gray-400" >{t("magic_link_desc")}</span>
|
||||
</div>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { magic_link: !magic_link })}
|
||||
@@ -105,10 +105,10 @@ export default function Logins() {
|
||||
<div className="row">
|
||||
<div className="title">
|
||||
<div className="txt">
|
||||
<Label>{t("google")}</Label>
|
||||
<Label className="dark:text-gray-200">{t("google")}</Label>
|
||||
<Tooltip link="https://doc.voce.chat/setting/third_login/login-google" />
|
||||
</div>
|
||||
<span className="desc">{t("google_desc")}</span>
|
||||
<span className="desc dark:!text-gray-400" >{t("google_desc")}</span>
|
||||
</div>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { google: !google })}
|
||||
@@ -128,10 +128,10 @@ export default function Logins() {
|
||||
<div className="row">
|
||||
<div className="title">
|
||||
<div className="txt">
|
||||
<Label>{t("github")}</Label>
|
||||
<Label className="dark:text-gray-200">{t("github")}</Label>
|
||||
<Tooltip link="https://doc.voce.chat/setting/third_login/login-github" />
|
||||
</div>
|
||||
<span className="desc">{t("github_desc")}</span>
|
||||
<span className="desc dark:!text-gray-400" >{t("github_desc")}</span>
|
||||
</div>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { github: !github })}
|
||||
@@ -159,10 +159,10 @@ export default function Logins() {
|
||||
<div className="row">
|
||||
<div className="title">
|
||||
<div className="txt">
|
||||
<Label>{t("metamask")}</Label>
|
||||
<Label className="dark:text-gray-200">{t("metamask")}</Label>
|
||||
<Tooltip link="https://doc.voce.chat/setting/third_login/login-metamask" />
|
||||
</div>
|
||||
<span className="desc">{t("metamask_desc")}</span>
|
||||
<span className="desc dark:!text-gray-400" >{t("metamask_desc")}</span>
|
||||
</div>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { metamask: !metamask })}
|
||||
@@ -174,10 +174,10 @@ export default function Logins() {
|
||||
<div className="row">
|
||||
<div className="title">
|
||||
<div className="txt">
|
||||
<Label htmlFor="desc">{t("oidc")}</Label>
|
||||
<Label className="dark:text-gray-200" htmlFor="desc">{t("oidc")}</Label>
|
||||
<Tooltip link="https://doc.voce.chat/setting/third_login/login-webid" />
|
||||
</div>
|
||||
<span className="desc">{t("oidc_desc")}</span>
|
||||
<span className="desc dark:!text-gray-400" >{t("oidc_desc")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
|
||||
@@ -57,11 +57,11 @@ export default function ConfigSMTP() {
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>{t("enable")}</Label>
|
||||
<Label className="dark:text-gray-200">{t("enable")}</Label>
|
||||
<Toggle onClick={toggleEnable} data-checked={enabled}></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="name">{t("host")}</Label>
|
||||
<Label className="dark:text-gray-200" htmlFor="name">{t("host")}</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="host"
|
||||
@@ -72,7 +72,7 @@ export default function ConfigSMTP() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("port")}</Label>
|
||||
<Label className="dark:text-gray-200" htmlFor="desc">{t("port")}</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
type={"number"}
|
||||
@@ -84,7 +84,7 @@ export default function ConfigSMTP() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("from")}</Label>
|
||||
<Label className="dark:text-gray-200" htmlFor="desc">{t("from")}</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="from"
|
||||
@@ -95,7 +95,7 @@ export default function ConfigSMTP() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("username")}</Label>
|
||||
<Label className="dark:text-gray-200" htmlFor="desc">{t("username")}</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="username"
|
||||
@@ -106,7 +106,7 @@ export default function ConfigSMTP() {
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">{t("password")}</Label>
|
||||
<Label className="dark:text-gray-200" htmlFor="desc">{t("password")}</Label>
|
||||
<Input
|
||||
type={"password"}
|
||||
disabled={!enabled}
|
||||
|
||||
@@ -86,11 +86,11 @@ const navs = [
|
||||
},
|
||||
{
|
||||
name: "terms",
|
||||
component: "Terms & Privacy"
|
||||
component: <div className="dark:text-white">Terms & Privacy</div>
|
||||
},
|
||||
{
|
||||
name: "feedback",
|
||||
component: <ul className="flex flex-col gap-2 text-lg">
|
||||
component: <ul className="flex flex-col gap-2 text-lg dark:text-white">
|
||||
<li>Email: <strong className="font-bold">han@privoce.com</strong></li>
|
||||
<li>Wechat: <strong className="font-bold">Privoce</strong></li>
|
||||
<li>GitHub:
|
||||
|
||||
Reference in New Issue
Block a user