mirror of
https://github.com/StanGirard/quivr.git
synced 2024-11-24 05:55:13 +03:00
feat: add brain creation step 2 (#1823)
Issue: https://github.com/StanGirard/quivr/issues/1770 Demo: https://github.com/StanGirard/quivr/assets/63923024/075aea35-892d-4103-8840-1a0721096745
This commit is contained in:
parent
8ba376e0a1
commit
27b2df8898
@ -5,6 +5,7 @@ import Button from "@/lib/components/ui/Button";
|
|||||||
import { Modal } from "@/lib/components/ui/Modal";
|
import { Modal } from "@/lib/components/ui/Modal";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
import { BrainParamsStep } from "./components/BrainParamsStep/BrainParamsStep";
|
||||||
import { BrainTypeSelectionStep } from "./components/BrainTypeSelectionStep/BrainTypeSelectionStep";
|
import { BrainTypeSelectionStep } from "./components/BrainTypeSelectionStep/BrainTypeSelectionStep";
|
||||||
import { Stepper } from "./components/Stepper/Stepper";
|
import { Stepper } from "./components/Stepper/Stepper";
|
||||||
import { useAddBrainConfig } from "./hooks/useAddBrainConfig";
|
import { useAddBrainConfig } from "./hooks/useAddBrainConfig";
|
||||||
@ -22,37 +23,38 @@ export const AddBrainSteps = ({
|
|||||||
useAddBrainConfig();
|
useAddBrainConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal
|
||||||
<Modal
|
Trigger={
|
||||||
Trigger={
|
<Button
|
||||||
<Button
|
onClick={() => void 0}
|
||||||
onClick={() => void 0}
|
variant={"tertiary"}
|
||||||
variant={"tertiary"}
|
className={cn("border-0", triggerClassName)}
|
||||||
className={cn("border-0", triggerClassName)}
|
data-testid="add-brain-button"
|
||||||
data-testid="add-brain-button"
|
|
||||||
>
|
|
||||||
{t("newBrain", { ns: "brain" })}
|
|
||||||
<MdAdd className="text-xl" />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
title={t("newBrainTitle", { ns: "brain" })}
|
|
||||||
desc={t("newBrainSubtitle", { ns: "brain" })}
|
|
||||||
isOpen={isBrainCreationModalOpened}
|
|
||||||
setOpen={setIsBrainCreationModalOpened}
|
|
||||||
CloseTrigger={<div />}
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
className="my-10 flex flex-col items-center gap-2 justify-center"
|
|
||||||
>
|
>
|
||||||
<Stepper />
|
{t("newBrain", { ns: "brain" })}
|
||||||
<BrainTypeSelectionStep
|
<MdAdd className="text-xl" />
|
||||||
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
</Button>
|
||||||
/>
|
}
|
||||||
</form>
|
title={t("newBrainTitle", { ns: "brain" })}
|
||||||
</Modal>
|
desc={t("newBrainSubtitle", { ns: "brain" })}
|
||||||
</>
|
isOpen={isBrainCreationModalOpened}
|
||||||
|
setOpen={setIsBrainCreationModalOpened}
|
||||||
|
CloseTrigger={<div />}
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
className="my-10 flex flex-col items-center gap-2 justify-center"
|
||||||
|
>
|
||||||
|
<Stepper />
|
||||||
|
<BrainTypeSelectionStep
|
||||||
|
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
||||||
|
/>
|
||||||
|
<BrainParamsStep
|
||||||
|
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
import { Fragment } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { FaArrowLeft, FaArrowRight } from "react-icons/fa";
|
||||||
|
|
||||||
|
import { PublicAccessConfirmationModal } from "@/lib/components/AddBrainModalOld/components/AddBrainConfig/components/PublicAccessConfirmationModal";
|
||||||
|
import Button from "@/lib/components/ui/Button";
|
||||||
|
import Field from "@/lib/components/ui/Field";
|
||||||
|
import { Radio } from "@/lib/components/ui/Radio";
|
||||||
|
import { TextArea } from "@/lib/components/ui/TextArea";
|
||||||
|
|
||||||
|
import { useBrainParamsStep } from "./hooks/useBrainParamsStep";
|
||||||
|
import { useBrainStatusOptions } from "./hooks/useBrainStatusOptions";
|
||||||
|
import { usePublicAccessConfirmationModal } from "./hooks/usePublicAccessConfirmationModal";
|
||||||
|
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||||
|
import { useBrainTypeSelectionStep } from "../BrainTypeSelectionStep/hooks/useBrainTypeSelectionStep";
|
||||||
|
|
||||||
|
type BrainParamsStepProps = {
|
||||||
|
onCancelBrainCreation: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BrainParamsStep = ({
|
||||||
|
onCancelBrainCreation,
|
||||||
|
}: BrainParamsStepProps): JSX.Element => {
|
||||||
|
const { goToNextStep, goToPreviousStep, currentStep } =
|
||||||
|
useBrainCreationSteps();
|
||||||
|
const { register } = useBrainTypeSelectionStep();
|
||||||
|
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||||
|
const { brainStatusOptions } = useBrainStatusOptions();
|
||||||
|
const { isNextButtonDisabled } = useBrainParamsStep();
|
||||||
|
const {
|
||||||
|
isPublicAccessConfirmationModalOpened,
|
||||||
|
onCancelPublicAccess,
|
||||||
|
onConfirmPublicAccess,
|
||||||
|
} = usePublicAccessConfirmationModal();
|
||||||
|
|
||||||
|
if (currentStep !== "BRAIN_PARAMS") {
|
||||||
|
return <Fragment />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Field
|
||||||
|
label={t("brainName", { ns: "brain" })}
|
||||||
|
autoFocus
|
||||||
|
placeholder={t("brainNamePlaceholder", { ns: "brain" })}
|
||||||
|
autoComplete="off"
|
||||||
|
className="flex-1"
|
||||||
|
required
|
||||||
|
{...register("name")}
|
||||||
|
/>
|
||||||
|
<TextArea
|
||||||
|
label={t("brainDescription", { ns: "brain" })}
|
||||||
|
placeholder={t("brainDescriptionPlaceholder", { ns: "brain" })}
|
||||||
|
autoComplete="off"
|
||||||
|
className="flex-1 m-3"
|
||||||
|
{...register("description")}
|
||||||
|
/>
|
||||||
|
<fieldset className="w-full flex flex-col">
|
||||||
|
<Radio
|
||||||
|
items={brainStatusOptions}
|
||||||
|
label={t("brain_status_label", { ns: "brain" })}
|
||||||
|
className="flex-1 justify-between w-[50%]"
|
||||||
|
{...register("status")}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
<div className="flex flex-row justify-between items-center flex-1 mt-10 w-full">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="tertiary"
|
||||||
|
onClick={onCancelBrainCreation}
|
||||||
|
className="text-primary"
|
||||||
|
>
|
||||||
|
{t("cancel", { ns: "translation" })}
|
||||||
|
</Button>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={goToPreviousStep}
|
||||||
|
className="py-2 border-primary text-primary"
|
||||||
|
>
|
||||||
|
<FaArrowLeft className="text-xl" size={16} />
|
||||||
|
{t("previous", { ns: "translation" })}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className="bg-primary text-white py-2 border-none"
|
||||||
|
type="button"
|
||||||
|
onClick={goToNextStep}
|
||||||
|
disabled={isNextButtonDisabled}
|
||||||
|
>
|
||||||
|
{t("next", { ns: "translation" })}
|
||||||
|
<FaArrowRight className="text-xl" size={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<PublicAccessConfirmationModal
|
||||||
|
opened={isPublicAccessConfirmationModalOpened}
|
||||||
|
onClose={onCancelPublicAccess}
|
||||||
|
onCancel={onCancelPublicAccess}
|
||||||
|
onConfirm={onConfirmPublicAccess}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
export const useBrainParamsStep = () => {
|
||||||
|
const { watch } = useFormContext<CreateBrainProps>();
|
||||||
|
const brainName = watch("name");
|
||||||
|
const description = watch("description");
|
||||||
|
|
||||||
|
const isNextButtonDisabled = brainName === "" || description === "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
isNextButtonDisabled,
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,25 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { BrainStatus } from "@/lib/types/brainConfig";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
export const useBrainStatusOptions = () => {
|
||||||
|
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||||
|
|
||||||
|
const brainStatusOptions: {
|
||||||
|
label: string;
|
||||||
|
value: BrainStatus;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
label: t("private_brain_label", { ns: "brain" }),
|
||||||
|
value: "private",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("public_brain_label", { ns: "brain" }),
|
||||||
|
value: "public",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
brainStatusOptions,
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,43 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
export const usePublicAccessConfirmationModal = () => {
|
||||||
|
const {
|
||||||
|
watch,
|
||||||
|
setValue,
|
||||||
|
formState: { dirtyFields },
|
||||||
|
} = useFormContext<CreateBrainProps>();
|
||||||
|
|
||||||
|
const [
|
||||||
|
isPublicAccessConfirmationModalOpened,
|
||||||
|
setIsPublicAccessConfirmationModalOpened,
|
||||||
|
] = useState(false);
|
||||||
|
|
||||||
|
const status = watch("status");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === "public" && dirtyFields.status === true) {
|
||||||
|
setIsPublicAccessConfirmationModalOpened(true);
|
||||||
|
}
|
||||||
|
}, [dirtyFields.status, status]);
|
||||||
|
|
||||||
|
const onConfirmPublicAccess = () => {
|
||||||
|
setIsPublicAccessConfirmationModalOpened(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancelPublicAccess = () => {
|
||||||
|
setValue("status", "private", {
|
||||||
|
shouldDirty: true,
|
||||||
|
});
|
||||||
|
setIsPublicAccessConfirmationModalOpened(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPublicAccessConfirmationModalOpened,
|
||||||
|
onConfirmPublicAccess,
|
||||||
|
onCancelPublicAccess,
|
||||||
|
};
|
||||||
|
};
|
@ -1,4 +1,5 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaArrowRight } from "react-icons/fa";
|
import { FaArrowRight } from "react-icons/fa";
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
import Button from "@/lib/components/ui/Button";
|
||||||
@ -18,7 +19,7 @@ export const BrainTypeSelectionStep = ({
|
|||||||
const { knowledgeSourceOptions } = useKnowledgeSourceLabel();
|
const { knowledgeSourceOptions } = useKnowledgeSourceLabel();
|
||||||
const { register } = useBrainTypeSelectionStep();
|
const { register } = useBrainTypeSelectionStep();
|
||||||
const { goToNextStep, currentStep } = useBrainCreationSteps();
|
const { goToNextStep, currentStep } = useBrainCreationSteps();
|
||||||
|
const { t } = useTranslation(["translation"]);
|
||||||
if (currentStep !== "BRAIN_TYPE") {
|
if (currentStep !== "BRAIN_TYPE") {
|
||||||
return <Fragment />;
|
return <Fragment />;
|
||||||
}
|
}
|
||||||
@ -36,7 +37,7 @@ export const BrainTypeSelectionStep = ({
|
|||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
onClick={onCancelBrainCreation}
|
onClick={onCancelBrainCreation}
|
||||||
>
|
>
|
||||||
Annuler
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -45,7 +46,7 @@ export const BrainTypeSelectionStep = ({
|
|||||||
data-testid="create-brain-submit-button"
|
data-testid="create-brain-submit-button"
|
||||||
onClick={goToNextStep}
|
onClick={goToNextStep}
|
||||||
>
|
>
|
||||||
Suivant
|
{t("next")}
|
||||||
<FaArrowRight className="text-xl" size={16} />
|
<FaArrowRight className="text-xl" size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const useBrainTypeSelectionStep = () => {
|
export const useBrainTypeSelectionStep = () => {
|
||||||
const { register } = useFormContext<CreateBrainProps>();
|
const { register, watch, reset, setValue } =
|
||||||
|
useFormContext<CreateBrainProps>();
|
||||||
|
const brainType = watch("brain_type");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentBrainType = brainType;
|
||||||
|
reset();
|
||||||
|
setValue("brain_type", currentBrainType);
|
||||||
|
}, [brainType, reset, setValue]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
register,
|
||||||
|
@ -2,6 +2,7 @@ import { Fragment } from "react";
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
import { Step } from "./components/Step";
|
||||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||||
|
|
||||||
export const Stepper = (): JSX.Element => {
|
export const Stepper = (): JSX.Element => {
|
||||||
@ -11,24 +12,7 @@ export const Stepper = (): JSX.Element => {
|
|||||||
<div className="flex flex-row justify-between w-full px-12 mb-12">
|
<div className="flex flex-row justify-between w-full px-12 mb-12">
|
||||||
{steps.map((step, index) => (
|
{steps.map((step, index) => (
|
||||||
<Fragment key={step.value}>
|
<Fragment key={step.value}>
|
||||||
<div
|
<Step index={index} step={step} />
|
||||||
key={step.label}
|
|
||||||
className="flex flex-row justify-center items-center flex-1"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col justify-center items-center">
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"h-[40px] w-[40px] border-solid rounded-full flex flex-row items-center justify-center mb-2 border-primary border-2",
|
|
||||||
step.value === currentStep ? "bg-primary text-white" : ""
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
<span key={step.label} className="text-xs text-center">
|
|
||||||
{step.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{index < steps.length - 1 && ( // Add horizontal line for all but the last step
|
{index < steps.length - 1 && ( // Add horizontal line for all but the last step
|
||||||
<hr
|
<hr
|
||||||
className={cn(
|
className={cn(
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
import { FaCheckCircle } from "react-icons/fa";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
import { useBrainCreationSteps } from "../../../hooks/useBrainCreationSteps";
|
||||||
|
import { Step as StepType } from "../../../types";
|
||||||
|
|
||||||
|
type StepProps = {
|
||||||
|
index: number;
|
||||||
|
step: StepType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Step = ({ index, step }: StepProps): JSX.Element => {
|
||||||
|
const { currentStep, currentStepIndex } = useBrainCreationSteps();
|
||||||
|
const isStepDone = index < currentStepIndex;
|
||||||
|
const stepContent = isStepDone ? <FaCheckCircle /> : index + 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={step.label}
|
||||||
|
className="flex flex-row justify-center items-center flex-1"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-center items-center">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"h-[40px] w-[40px] border-solid rounded-full flex flex-row items-center justify-center mb-2 border-primary border-2 text-primary",
|
||||||
|
isStepDone ? "bg-primary text-white" : "",
|
||||||
|
step.value === currentStep ? "bg-primary text-white" : ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{stepContent}
|
||||||
|
</div>
|
||||||
|
<span key={step.label} className="text-xs text-center">
|
||||||
|
{step.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -3,13 +3,13 @@ import { useTranslation } from "react-i18next";
|
|||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
||||||
|
|
||||||
import { StepperStep } from "../types";
|
import { Step } from "../types";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const useBrainCreationSteps = () => {
|
export const useBrainCreationSteps = () => {
|
||||||
const { t } = useTranslation("brain");
|
const { t } = useTranslation("brain");
|
||||||
|
|
||||||
const steps: StepperStep[] = [
|
const steps: Step[] = [
|
||||||
{
|
{
|
||||||
label: t("brain_type"),
|
label: t("brain_type"),
|
||||||
value: "BRAIN_TYPE",
|
value: "BRAIN_TYPE",
|
||||||
@ -25,11 +25,11 @@ export const useBrainCreationSteps = () => {
|
|||||||
];
|
];
|
||||||
const { watch, setValue } = useFormContext<CreateBrainProps>();
|
const { watch, setValue } = useFormContext<CreateBrainProps>();
|
||||||
const currentStep = watch("brainCreationStep");
|
const currentStep = watch("brainCreationStep");
|
||||||
|
const currentStepIndex = steps.findIndex(
|
||||||
|
(step) => step.value === currentStep
|
||||||
|
);
|
||||||
|
|
||||||
const goToNextStep = () => {
|
const goToNextStep = () => {
|
||||||
const currentStepIndex = steps.findIndex(
|
|
||||||
(step) => step.value === currentStep
|
|
||||||
);
|
|
||||||
if (currentStepIndex === -1 || currentStepIndex === steps.length - 1) {
|
if (currentStepIndex === -1 || currentStepIndex === steps.length - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -39,9 +39,6 @@ export const useBrainCreationSteps = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const goToPreviousStep = () => {
|
const goToPreviousStep = () => {
|
||||||
const currentStepIndex = steps.findIndex(
|
|
||||||
(step) => step.value === currentStep
|
|
||||||
);
|
|
||||||
if (currentStepIndex === -1 || currentStepIndex === 0) {
|
if (currentStepIndex === -1 || currentStepIndex === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -55,5 +52,6 @@ export const useBrainCreationSteps = () => {
|
|||||||
steps,
|
steps,
|
||||||
goToNextStep,
|
goToNextStep,
|
||||||
goToPreviousStep,
|
goToPreviousStep,
|
||||||
|
currentStepIndex,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BrainCreationStep } from "../../types";
|
import { BrainCreationStep } from "../../types";
|
||||||
|
|
||||||
export type StepperStep = {
|
export type Step = {
|
||||||
label: string;
|
label: string;
|
||||||
value: BrainCreationStep;
|
value: BrainCreationStep;
|
||||||
};
|
};
|
||||||
|
@ -30,5 +30,8 @@
|
|||||||
"invalidUrl": "Invalid URL",
|
"invalidUrl": "Invalid URL",
|
||||||
"crawlButton": "Crawl",
|
"crawlButton": "Crawl",
|
||||||
"themeSelect": "Interface theme",
|
"themeSelect": "Interface theme",
|
||||||
"languageSelect": "Preferred language"
|
"languageSelect": "Preferred language",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"next": "Next",
|
||||||
|
"previous": "Previous"
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,8 @@
|
|||||||
"invalidUrl": "URL inválida",
|
"invalidUrl": "URL inválida",
|
||||||
"crawlButton": "Rastrear",
|
"crawlButton": "Rastrear",
|
||||||
"themeSelect": "Tema de interfaz",
|
"themeSelect": "Tema de interfaz",
|
||||||
"languageSelect": "Idioma preferido"
|
"languageSelect": "Idioma preferido",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"next": "Siguiente",
|
||||||
|
"previous": "Anterior"
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
"email": "Email",
|
"email": "Email",
|
||||||
"or": "ou",
|
"or": "ou",
|
||||||
"and": "et",
|
"and": "et",
|
||||||
|
|
||||||
"logoutButton": "Déconnexion",
|
"logoutButton": "Déconnexion",
|
||||||
"updateButton": "Mettre à jour",
|
"updateButton": "Mettre à jour",
|
||||||
"uploadButton": "Télécharger",
|
"uploadButton": "Télécharger",
|
||||||
@ -31,5 +30,8 @@
|
|||||||
"invalidUrl": "URL invalide",
|
"invalidUrl": "URL invalide",
|
||||||
"crawlButton": "Crawler",
|
"crawlButton": "Crawler",
|
||||||
"themeSelect": "Thème de l'interface",
|
"themeSelect": "Thème de l'interface",
|
||||||
"languageSelect": "Langue préférée"
|
"languageSelect": "Langue préférée",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"next": "Suivant",
|
||||||
|
"previous": "Précédent"
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,8 @@
|
|||||||
"invalidUrl": "URL inválida",
|
"invalidUrl": "URL inválida",
|
||||||
"crawlButton": "Rastrear",
|
"crawlButton": "Rastrear",
|
||||||
"themeSelect": "Tema de interface",
|
"themeSelect": "Tema de interface",
|
||||||
"languageSelect": "Língua preferida"
|
"languageSelect": "Língua preferida",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"next": "Próximo",
|
||||||
|
"previous": "Anterior"
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,8 @@
|
|||||||
"invalidUrl": "Неверный URL",
|
"invalidUrl": "Неверный URL",
|
||||||
"crawlButton": "Поиск",
|
"crawlButton": "Поиск",
|
||||||
"themeSelect": "Тема интерфейса",
|
"themeSelect": "Тема интерфейса",
|
||||||
"languageSelect": "предпочтительный язык"
|
"languageSelect": "предпочтительный язык",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"next": "Далее",
|
||||||
|
"previous": "Назад"
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,8 @@
|
|||||||
"invalidUrl": "无效的 URL",
|
"invalidUrl": "无效的 URL",
|
||||||
"crawlButton": "爬取",
|
"crawlButton": "爬取",
|
||||||
"themeSelect": "主题",
|
"themeSelect": "主题",
|
||||||
"languageSelect": "首选语言"
|
"languageSelect": "首选语言",
|
||||||
|
"cancel": "取消",
|
||||||
|
"next": "下一个",
|
||||||
|
"previous": "上一个"
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user