refactor: widget setting
This commit is contained in:
@@ -1,194 +0,0 @@
|
|||||||
// import { useState, MouseEvent } from "react";
|
|
||||||
// import dayjs from "dayjs";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
||||||
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
||||||
|
|
||||||
import IconQuestion from "@/assets/icons/question.svg";
|
|
||||||
import { useAppSelector } from "../../app/store";
|
|
||||||
import Button from "../../components/styled/Button";
|
|
||||||
import Input from "../../components/styled/Input";
|
|
||||||
import useCopy from "../../hooks/useCopy";
|
|
||||||
import { shallowEqual } from "react-redux";
|
|
||||||
import LinkifyText from "@/components/LinkifyText";
|
|
||||||
|
|
||||||
const Row = ({
|
|
||||||
paramKey,
|
|
||||||
paramDefault,
|
|
||||||
remarks
|
|
||||||
}: {
|
|
||||||
paramKey: string;
|
|
||||||
paramDefault: string | number;
|
|
||||||
remarks: string;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<tr className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-50 border-b transition duration-300 ease-in-out md:hover:bg-gray-100 dark:md:hover:bg-gray-900">
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">{paramKey}</td>
|
|
||||||
<td className="text-sm font-light px-6 py-4 whitespace-nowrap">
|
|
||||||
{paramKey == "theme-color" ? (
|
|
||||||
<span style={{ color: paramDefault as string }}> {paramDefault}</span>
|
|
||||||
) : (
|
|
||||||
paramDefault
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="text-sm font-light px-6 py-4">
|
|
||||||
<LinkifyText linkPreview={false} text={remarks} mention={false} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default function Widget() {
|
|
||||||
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
|
||||||
const widgetLink = `${location.origin}/widget.html?host=${loginUid}`;
|
|
||||||
const { t } = useTranslation("setting", { keyPrefix: "widget" });
|
|
||||||
const { t: wt } = useTranslation("widget");
|
|
||||||
const { t: ct } = useTranslation();
|
|
||||||
const { copied, copy } = useCopy({ enableToast: false });
|
|
||||||
const copyLink = () => {
|
|
||||||
copy(widgetLink);
|
|
||||||
};
|
|
||||||
// const disableBtn = !reachLimit;
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col justify-start items-start">
|
|
||||||
<div className="font-semibold dark:text-white">{t("tip")}</div>
|
|
||||||
<label htmlFor="code" className="font-semibold dark:text-white">
|
|
||||||
{t("code")}:
|
|
||||||
</label>
|
|
||||||
<SyntaxHighlighter id="code" language="html" style={vscDarkPlus} className="rounded">
|
|
||||||
{`<!-- ${t(
|
|
||||||
"code_comment"
|
|
||||||
)} -->\n<script \n data-host-id="${loginUid}" \n data-auto-reg="true" \n data-login-token="" \n data-title="" \n data-logo="" \n data-theme-color="#1fe1f9" \n data-close-width="48" \n data-close-height="48" \n data-open-width="380" \n data-open-height="680" \n data-welcome="Your custom welcome text" \n src="${
|
|
||||||
location.origin
|
|
||||||
}/widget.js" \n async \n></script>`}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
<div className="text-sm dark:text-white">{t("custom_style_tip")}:</div>
|
|
||||||
<SyntaxHighlighter id="code" language="html" style={vscDarkPlus} className="rounded">
|
|
||||||
{`<style>
|
|
||||||
#VOCECHAT_WIDGET {
|
|
||||||
left:10px /* left position */
|
|
||||||
bottom: 5px /* bottom position */
|
|
||||||
}
|
|
||||||
</style>`}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
<div className="font-semibold dark:text-white mt-5 mb-2">{t("config")}:</div>
|
|
||||||
<div className="w-full md:w-[700px] border border-solid border-gray-300 dark:border-gray-400 rounded overflow-auto md:overflow-hidden">
|
|
||||||
<table className="min-w-full table-auto">
|
|
||||||
<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 dark:text-white px-6 py-4 text-left whitespace-nowrap"
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{[
|
|
||||||
{
|
|
||||||
paramKey: "id",
|
|
||||||
paramDefault: "-",
|
|
||||||
remarks: t("param_id")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "host-id",
|
|
||||||
paramDefault: 1,
|
|
||||||
remarks: t("param_host")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "auto-reg",
|
|
||||||
paramDefault: "true",
|
|
||||||
remarks: t("param_auto_reg")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "login-token",
|
|
||||||
paramDefault: `-`,
|
|
||||||
remarks: t("param_login_token")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "title",
|
|
||||||
paramDefault: `[VoceChat Name]`,
|
|
||||||
remarks: t("param_title")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "logo",
|
|
||||||
paramDefault: `[VoceChat Logo]`,
|
|
||||||
remarks: t("param_logo")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "theme-color",
|
|
||||||
paramDefault: "#1fe1f9",
|
|
||||||
remarks: t("param_theme_color")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "close-width",
|
|
||||||
paramDefault: `48(px)`,
|
|
||||||
remarks: t("param_close_width")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "close-height",
|
|
||||||
paramDefault: `48(px)`,
|
|
||||||
remarks: t("param_close_height")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "open-width",
|
|
||||||
paramDefault: `380(px)`,
|
|
||||||
remarks: t("param_open_width")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "open-height",
|
|
||||||
paramDefault: `680(px)`,
|
|
||||||
remarks: t("param_open_height")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "position",
|
|
||||||
paramDefault: `right`,
|
|
||||||
remarks: t("param_position")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paramKey: "welcome",
|
|
||||||
paramDefault: wt("welcome"),
|
|
||||||
remarks: t("param_welcome")
|
|
||||||
}
|
|
||||||
].map((row) => (
|
|
||||||
<Row key={row.paramKey} {...row} />
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
<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 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>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div className="mt-8">
|
|
||||||
<p className="text-sm mb-2 text-gray-500 dark:text-gray-50 flex flex-col md:flex-row gap-4">
|
|
||||||
{t("share_link")}{" "}
|
|
||||||
<a
|
|
||||||
className="text-primary-500 flex gap-1 items-center"
|
|
||||||
href="https://doc.voce.chat/widget"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<IconQuestion /> {t("widget_faq")}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<div className="w-full md:w-96 mb-3 relative">
|
|
||||||
<Input readOnly className={"large !pr-16"} value={widgetLink} />
|
|
||||||
<Button
|
|
||||||
onClick={copyLink}
|
|
||||||
className="ghost small border_less absolute right-1 top-1/2 -translate-y-1/2 dark:!text-primary-400"
|
|
||||||
>
|
|
||||||
{copied ? "Copied" : ct("action.copy")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import LinkifyText from "@/components/LinkifyText";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const Row = ({
|
||||||
|
paramKey,
|
||||||
|
paramDefault,
|
||||||
|
remarks
|
||||||
|
}: {
|
||||||
|
paramKey: string;
|
||||||
|
paramDefault: string | number;
|
||||||
|
remarks: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<tr className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-50 border-b transition duration-300 ease-in-out md:hover:bg-gray-100 dark:md:hover:bg-gray-900">
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">{paramKey}</td>
|
||||||
|
<td className="text-sm font-light px-6 py-4 whitespace-nowrap">
|
||||||
|
{paramKey == "theme-color" ? (
|
||||||
|
<span style={{ color: paramDefault as string }}> {paramDefault}</span>
|
||||||
|
) : (
|
||||||
|
paramDefault
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="text-sm font-light px-6 py-4">
|
||||||
|
<LinkifyText linkPreview={false} text={remarks} mention={false} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
const ConfigDetails = ({}: Props) => {
|
||||||
|
const { t } = useTranslation("setting", { keyPrefix: "widget" });
|
||||||
|
const { t: wt } = useTranslation("widget");
|
||||||
|
return (
|
||||||
|
<table className="min-w-full table-auto">
|
||||||
|
<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 dark:text-white px-6 py-4 text-left whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
paramKey: "id",
|
||||||
|
paramDefault: "-",
|
||||||
|
remarks: t("param_id")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "host-id",
|
||||||
|
paramDefault: 1,
|
||||||
|
remarks: t("param_host")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "auto-reg",
|
||||||
|
paramDefault: "true",
|
||||||
|
remarks: t("param_auto_reg")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "login-token",
|
||||||
|
paramDefault: `-`,
|
||||||
|
remarks: t("param_login_token")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "title",
|
||||||
|
paramDefault: `[VoceChat Name]`,
|
||||||
|
remarks: t("param_title")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "logo",
|
||||||
|
paramDefault: `[VoceChat Logo]`,
|
||||||
|
remarks: t("param_logo")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "theme-color",
|
||||||
|
paramDefault: "#1fe1f9",
|
||||||
|
remarks: t("param_theme_color")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "close-width",
|
||||||
|
paramDefault: `48(px)`,
|
||||||
|
remarks: t("param_close_width")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "close-height",
|
||||||
|
paramDefault: `48(px)`,
|
||||||
|
remarks: t("param_close_height")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "open-width",
|
||||||
|
paramDefault: `380(px)`,
|
||||||
|
remarks: t("param_open_width")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "open-height",
|
||||||
|
paramDefault: `680(px)`,
|
||||||
|
remarks: t("param_open_height")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "position",
|
||||||
|
paramDefault: `right`,
|
||||||
|
remarks: t("param_position")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paramKey: "welcome",
|
||||||
|
paramDefault: wt("welcome"),
|
||||||
|
remarks: t("param_welcome")
|
||||||
|
}
|
||||||
|
].map((row) => (
|
||||||
|
<Row key={row.paramKey} {...row} />
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
<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 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>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfigDetails;
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// import { useState, MouseEvent } from "react";
|
||||||
|
// import dayjs from "dayjs";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
|
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||||
|
|
||||||
|
import IconQuestion from "@/assets/icons/question.svg";
|
||||||
|
import { useAppSelector } from "@/app/store";
|
||||||
|
import Button from "@/components/styled/Button";
|
||||||
|
import Input from "@/components/styled/Input";
|
||||||
|
import useCopy from "@/hooks/useCopy";
|
||||||
|
import { shallowEqual } from "react-redux";
|
||||||
|
import ConfigDetails from "./ConfigDetails";
|
||||||
|
|
||||||
|
export default function Widget() {
|
||||||
|
const loginUid = useAppSelector((store) => store.authData.user?.uid, shallowEqual);
|
||||||
|
const widgetLink = `${location.origin}/widget.html?host=${loginUid}`;
|
||||||
|
const { t } = useTranslation("setting", { keyPrefix: "widget" });
|
||||||
|
const { t: ct } = useTranslation();
|
||||||
|
const { copied, copy } = useCopy({ enableToast: false });
|
||||||
|
const copyLink = () => {
|
||||||
|
copy(widgetLink);
|
||||||
|
};
|
||||||
|
// const disableBtn = !reachLimit;
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col justify-start items-start">
|
||||||
|
<div className="font-semibold dark:text-white">{t("tip")}</div>
|
||||||
|
<label htmlFor="code" className="font-semibold dark:text-white">
|
||||||
|
{t("code")}:
|
||||||
|
</label>
|
||||||
|
<SyntaxHighlighter id="code" language="html" style={vscDarkPlus} className="rounded">
|
||||||
|
{`<!-- ${t(
|
||||||
|
"code_comment"
|
||||||
|
)} -->\n<script \n data-host-id="${loginUid}" \n data-auto-reg="true" \n data-login-token="" \n data-title="" \n data-logo="" \n data-theme-color="#1fe1f9" \n data-close-width="48" \n data-close-height="48" \n data-open-width="380" \n data-open-height="680" \n data-welcome="Your custom welcome text" \n src="${
|
||||||
|
location.origin
|
||||||
|
}/widget.js" \n async \n></script>`}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
<div className="text-sm dark:text-white">{t("custom_style_tip")}:</div>
|
||||||
|
<SyntaxHighlighter id="code" language="html" style={vscDarkPlus} className="rounded">
|
||||||
|
{`<style>
|
||||||
|
#VOCECHAT_WIDGET {
|
||||||
|
left:10px /* left position */
|
||||||
|
bottom: 5px /* bottom position */
|
||||||
|
}
|
||||||
|
</style>`}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
<div className="font-semibold dark:text-white mt-5 mb-2">{t("config")}:</div>
|
||||||
|
<div className="w-full md:w-[700px] border border-solid border-gray-300 dark:border-gray-400 rounded overflow-auto md:overflow-hidden">
|
||||||
|
{/* 配置说明 */}
|
||||||
|
<ConfigDetails />
|
||||||
|
</div>
|
||||||
|
<div className="mt-8">
|
||||||
|
<p className="text-sm mb-2 text-gray-500 dark:text-gray-50 flex flex-col md:flex-row gap-4">
|
||||||
|
{t("share_link")}{" "}
|
||||||
|
<a
|
||||||
|
className="text-primary-500 flex gap-1 items-center"
|
||||||
|
href="https://doc.voce.chat/widget"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<IconQuestion /> {t("widget_faq")}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div className="w-full md:w-96 mb-3 relative">
|
||||||
|
<Input readOnly className={"large !pr-16"} value={widgetLink} />
|
||||||
|
<Button
|
||||||
|
onClick={copyLink}
|
||||||
|
className="ghost small border_less absolute right-1 top-1/2 -translate-y-1/2 dark:!text-primary-400"
|
||||||
|
>
|
||||||
|
{copied ? "Copied" : ct("action.copy")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user