feat:update form konten, spit,task
This commit is contained in:
parent
39dd1e71bf
commit
17a29cb7e7
|
|
@ -0,0 +1,18 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormImageDetail from "@/components/form/content/image-detail-form";
|
||||
import FormImageUpdate from "@/components/form/content/image-update-form";
|
||||
import FormAudioUpdate from "@/components/form/content/audio-update-form";
|
||||
import FormAudioSeo from "@/components/form/content/audio-update-seo";
|
||||
|
||||
const AudioUpdatePage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormAudioSeo />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AudioUpdatePage;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormImageDetail from "@/components/form/content/image-detail-form";
|
||||
import FormImageUpdate from "@/components/form/content/image-update-form";
|
||||
import FormImageSeo from "@/components/form/content/image-update-seo";
|
||||
|
||||
const ImageUpdateSeoPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormImageSeo />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageUpdateSeoPage;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormTeksUpdate from "@/components/form/content/teks-update-form";
|
||||
import FormTeksSeo from "@/components/form/content/teks-update-seo";
|
||||
|
||||
const TeksUpdatePage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormTeksSeo />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeksUpdatePage;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import FormImageDetail from "@/components/form/content/image-detail-form";
|
||||
import FormImageUpdate from "@/components/form/content/image-update-form";
|
||||
import FormVideoUpdate from "@/components/form/content/video-update-form";
|
||||
import FormVideoSeo from "@/components/form/content/video-update-seo";
|
||||
|
||||
const VideoUpdatePage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<FormVideoSeo />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VideoUpdatePage;
|
||||
|
|
@ -73,7 +73,7 @@ const TaskTable = () => {
|
|||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||
const [dateFilter, setDateFilter] = React.useState("");
|
||||
const [endDate, setEndDate] = React.useState("");
|
||||
const [filterByCode, setFilterByCode] = React.useState<string>("");
|
||||
|
|
@ -113,7 +113,15 @@ const TaskTable = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, [page, limit, isSpecificAttention, search, dateFilter, filterByCode]);
|
||||
}, [
|
||||
page,
|
||||
limit,
|
||||
isSpecificAttention,
|
||||
search,
|
||||
dateFilter,
|
||||
filterByCode,
|
||||
statusFilter,
|
||||
]);
|
||||
|
||||
async function fetchData() {
|
||||
const formattedStartDate = dateFilter
|
||||
|
|
@ -126,10 +134,22 @@ const TaskTable = () => {
|
|||
limit,
|
||||
filterByCode,
|
||||
formattedStartDate,
|
||||
isSpecificAttention ? "atensi-khusus" : "tugas-harian"
|
||||
isSpecificAttention ? "atensi-khusus" : "tugas-harian",
|
||||
statusFilter
|
||||
);
|
||||
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
|
||||
// let contentDataFilter = res?.data?.data?.content || [];
|
||||
|
||||
// Filter berdasarkan status
|
||||
// contentDataFilter = contentDataFilter.filter((item: any) => {
|
||||
// const isSelesai = statusFilter.includes(1) ? item.isDone : true;
|
||||
// const isAktif = statusFilter.includes(2) ? item.isActive : true;
|
||||
// return isSelesai && isAktif;
|
||||
// });
|
||||
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * limit + index + 1;
|
||||
});
|
||||
|
|
@ -145,24 +165,25 @@ const TaskTable = () => {
|
|||
}
|
||||
|
||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value); // Perbarui state search
|
||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||
setFilterByCode(e.target.value);
|
||||
setSearch(e.target.value);
|
||||
table.getColumn("judul")?.setFilterValue(e.target.value);
|
||||
};
|
||||
|
||||
function handleStatusCheckboxChange(value: any) {
|
||||
setStatusFilter((prev: any) =>
|
||||
function handleStatusCheckboxChange(value: number) {
|
||||
setStatusFilter((prev) =>
|
||||
prev.includes(value)
|
||||
? prev.filter((status: any) => status !== value)
|
||||
? prev.filter((status) => status !== value)
|
||||
: [...prev, value]
|
||||
);
|
||||
}
|
||||
|
||||
const handleSearchFilterByCode = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
console.log("code :", value);
|
||||
setFilterByCode(value);
|
||||
fetchData();
|
||||
};
|
||||
// const handleSearchFilterByCode = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// const value = e.target.value;
|
||||
// console.log("code :", value);
|
||||
// setFilterByCode(value);
|
||||
// fetchData();
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
|
|
@ -211,7 +232,7 @@ const TaskTable = () => {
|
|||
</InputGroupText>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search Judul..."
|
||||
placeholder="Search Judul dan Code"
|
||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white w-full"
|
||||
value={search}
|
||||
onChange={handleSearch}
|
||||
|
|
@ -243,25 +264,25 @@ const TaskTable = () => {
|
|||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-2 my-1">
|
||||
{/* <div className="mx-2 my-1">
|
||||
<Label>Code</Label>
|
||||
<Input
|
||||
placeholder="Filter Status..."
|
||||
value={filterByCode}
|
||||
onChange={handleSearchFilterByCode}
|
||||
// onChange={handleSearchFilterByCode}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
<Label className="ml-2 mt-2">Status</Label>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="status-2"
|
||||
id="status-1"
|
||||
className="mr-2"
|
||||
checked={statusFilter.includes(1)}
|
||||
onChange={() => handleStatusCheckboxChange(1)}
|
||||
/>
|
||||
<label htmlFor="status-2" className="text-sm">
|
||||
<label htmlFor="status-1" className="text-sm">
|
||||
Selesai
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -281,7 +302,7 @@ const TaskTable = () => {
|
|||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
{/* <div className="flex-none">
|
||||
<Input
|
||||
placeholder="Filter Status..."
|
||||
value={
|
||||
|
|
@ -292,7 +313,7 @@ const TaskTable = () => {
|
|||
}
|
||||
className="max-w-sm "
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
<Table className="overflow-hidden mt-3">
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
|
|||
import { Item } from "@radix-ui/react-dropdown-menu";
|
||||
import dynamic from "next/dynamic";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -921,7 +922,7 @@ export default function FormAudio() {
|
|||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{id}
|
||||
{"Narasi " + (index + 1)}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -930,18 +931,8 @@ export default function FormAudio() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<a
|
||||
href={`/admin/media/${
|
||||
fileTypeId === "1"
|
||||
? "image"
|
||||
: fileTypeId === "2"
|
||||
? "video"
|
||||
: fileTypeId === "3"
|
||||
? "text"
|
||||
: "audio"
|
||||
}/update-new/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href={`/contributor/content/audio/update-seo/${selectedArticleId}`}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
@ -951,7 +942,7 @@ export default function FormAudio() {
|
|||
>
|
||||
Edit
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,757 @@
|
|||
"use client";
|
||||
import React, {
|
||||
ChangeEvent,
|
||||
Fragment,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
|
||||
import Cookies from "js-cookie";
|
||||
import {
|
||||
createMedia,
|
||||
deleteFile,
|
||||
deleteMedia,
|
||||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
uploadThumbnail,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import Image from "next/image";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Upload } from "tus-js-client";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import ViewEditor from "@/components/editor/view-editor";
|
||||
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
|
||||
import { Gauge } from "@mui/x-charts/Gauge";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { Pie } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ChartOptions,
|
||||
} from "chart.js";
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend);
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type Detail = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
category: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
publishedFor: string;
|
||||
|
||||
publishedForObject: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
htmlDescription: string;
|
||||
creatorName: string;
|
||||
categoryName: string;
|
||||
thumbnailLink: string;
|
||||
tags: string;
|
||||
};
|
||||
|
||||
type Option = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
}
|
||||
|
||||
export default function FormAudioSeo() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [selectedOptions, setSelectedOptions] = useState<{
|
||||
[fileId: number]: string;
|
||||
}>({});
|
||||
const [articleData, setArticleData] = useState({
|
||||
title: "",
|
||||
mainKeyword: "",
|
||||
additionalKeywords: "",
|
||||
metaTitle: "",
|
||||
metaDescription: "",
|
||||
});
|
||||
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
|
||||
const [errorSEO, setErrorSEO] = useState<string[]>([]);
|
||||
const [warningSEO, setWarningSEO] = useState<string[]>([]);
|
||||
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
|
||||
// const [errorData, setErrorData] = useState<string[]>([]);
|
||||
const [warningData, setWarningData] = useState<string[]>([]);
|
||||
const [optimizedData, setOptimizedData] = useState<string[]>([]);
|
||||
const [errorsData, setErrorData] = useState<string[]>([]);
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
|
||||
detail?.category.id
|
||||
);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
let fileTypeId = "1";
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
});
|
||||
|
||||
// const handleKeyDown = (e: any) => {
|
||||
// const newTag = e.target.value.trim(); // Ambil nilai input
|
||||
// if (e.key === "Enter" && newTag) {
|
||||
// e.preventDefault(); // Hentikan submit form
|
||||
// if (!tags.includes(newTag)) {
|
||||
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
|
||||
// setValue("tags", ""); // Kosongkan input
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
const files = Array.from(event.target.files);
|
||||
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
|
||||
console.log("DATAFILE::", selectedFiles);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// const handleCheckboxChange = (id: number) => {
|
||||
// setSelectedPublishers((prev) =>
|
||||
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
// );
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getCategories();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const getCategories = async () => {
|
||||
try {
|
||||
const category = await listEnableCategory(fileTypeId);
|
||||
const resCategory: Category[] = category?.data?.data?.content;
|
||||
|
||||
setCategories(resCategory);
|
||||
console.log("data category", resCategory);
|
||||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
// setValue("categoryId", findCategory.id);
|
||||
setSelectedCategory(findCategory.id); // Set the selected category
|
||||
const response = await getTagsBySubCategoryId(findCategory.id);
|
||||
setTags(response?.data?.data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch categories:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchArticleData = async () => {
|
||||
try {
|
||||
const response = await getDetailArticle(id);
|
||||
const data = response?.data?.data;
|
||||
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
|
||||
|
||||
setArticleData({
|
||||
title: data.title || "",
|
||||
mainKeyword: data.mainKeyword || "",
|
||||
additionalKeywords: data.additionalKeywords || "",
|
||||
metaTitle: data.metaTitle || "",
|
||||
metaDescription: data.metaDescription || "",
|
||||
});
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
|
||||
// reset({
|
||||
// title: data.title,
|
||||
// mainKeyword: data.mainKeyword,
|
||||
// additionalKeywords: data.additionalKeywords,
|
||||
// metaTitle: data.metaTitle,
|
||||
// metaDescription: data.metaDescription,
|
||||
// });
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch article data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
if (id) {
|
||||
fetchArticleData();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSeoScore = async () => {
|
||||
const res = await getSeoScore(id);
|
||||
if (res.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
|
||||
let errorList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
|
||||
];
|
||||
setErrorSEO(errorList);
|
||||
let warningList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
|
||||
];
|
||||
setWarningSEO(warningList);
|
||||
let optimizedList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
|
||||
?.optimized,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
|
||||
];
|
||||
setOptimizedSEO(optimizedList);
|
||||
setErrorData(errorList);
|
||||
setWarningData(warningList);
|
||||
setOptimizedData(optimizedList);
|
||||
};
|
||||
fetchSeoScore();
|
||||
});
|
||||
|
||||
const data = {
|
||||
labels: ["SEO Score (" + totalScoreSEO + "%)"],
|
||||
datasets: [
|
||||
{
|
||||
data: [totalScoreSEO],
|
||||
backgroundColor: ["#4CAF50"],
|
||||
hoverBackgroundColor: ["#388E3C"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const options: ChartOptions<"pie"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "bottom", // TypeScript now correctly recognizes this as a valid option
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const requestData = {
|
||||
...data,
|
||||
id: detail?.id,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
htmlDescription: data.description,
|
||||
fileTypeId,
|
||||
categoryId: selectedTarget,
|
||||
subCategoryId: selectedTarget,
|
||||
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
|
||||
statusId: "1",
|
||||
publishedFor: publishedFor.join(","),
|
||||
creatorName: data.creatorName,
|
||||
tags: finalTags,
|
||||
isYoutube: false,
|
||||
isInternationalMedia: false,
|
||||
};
|
||||
|
||||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
const formMedia = new FormData();
|
||||
const thumbnail = files[0];
|
||||
formMedia.append("file", thumbnail);
|
||||
const responseThumbnail = await uploadThumbnail(id, formMedia);
|
||||
if (responseThumbnail?.error == true) {
|
||||
error(responseThumbnail?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const progressInfoArr = [];
|
||||
for (const item of files) {
|
||||
progressInfoArr.push({ percentage: 0, fileName: item.name });
|
||||
}
|
||||
progressInfo = progressInfoArr;
|
||||
setIsStartUpload(true);
|
||||
setProgressList(progressInfoArr);
|
||||
|
||||
close();
|
||||
// showProgress();
|
||||
files.map(async (item: any, index: number) => {
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
);
|
||||
});
|
||||
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
async function uploadResumableFile(
|
||||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
duration: string
|
||||
) {
|
||||
console.log(idx, id, file, duration);
|
||||
|
||||
// const placements = getPlacement(file.placements);
|
||||
// console.log("Placementttt: : ", placements);
|
||||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
metadata: {
|
||||
mediaid: id,
|
||||
filename: file.name,
|
||||
filetype: file.type,
|
||||
duration,
|
||||
isWatermark: "true", // hardcode
|
||||
},
|
||||
onBeforeRequest: function (req) {
|
||||
var xhr = req.getUnderlyingObject();
|
||||
xhr.withCredentials = true;
|
||||
},
|
||||
onError: async (e: any) => {
|
||||
console.log("Error upload :", e);
|
||||
error(e);
|
||||
},
|
||||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
) => {
|
||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
progressInfo[idx].percentage = uploadPersen;
|
||||
counterUpdateProgress++;
|
||||
console.log(counterUpdateProgress);
|
||||
setProgressList(progressInfo);
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
uploadPersen = 100;
|
||||
progressInfo[idx].percentage = 100;
|
||||
counterUpdateProgress++;
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
successTodo();
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
}
|
||||
|
||||
const onSubmit = (data: ImageSchema) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const successSubmit = (redirect: string) => {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
});
|
||||
};
|
||||
|
||||
function successTodo() {
|
||||
let counter = 0;
|
||||
for (const element of progressInfo) {
|
||||
if (element.percentage == 100) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
if (counter == progressInfo.length) {
|
||||
setIsStartUpload(false);
|
||||
// hideProgress();
|
||||
Cookies.remove("idCreate");
|
||||
successSubmit("/in/contributor/content/image/");
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveFile = (file: FileWithPreview) => {
|
||||
const uploadedFiles = files;
|
||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
setFiles([...filtered]);
|
||||
};
|
||||
|
||||
function success() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleDeleteFile = (id: number) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus file",
|
||||
text: "Apakah Anda yakin ingin menghapus file ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDelete(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async function doDelete(id: number) {
|
||||
const data = { id };
|
||||
|
||||
try {
|
||||
const response = await deleteFile(data);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Jika berhasil, hapus file dari state lokal
|
||||
setFiles((prevFiles: any) =>
|
||||
prevFiles.filter((file: any) => file.id !== id)
|
||||
);
|
||||
success();
|
||||
} catch (err) {
|
||||
error("Terjadi kesalahan saat menghapus file");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-5 mb-3">
|
||||
<Card>
|
||||
<Tabs defaultValue="content" className="">
|
||||
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Konten
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="checker"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Checker
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="content">
|
||||
{articleData !== undefined ? (
|
||||
<CardContent className="space-y-2 my-3">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="name">Judul</Label>
|
||||
<Input id="name" defaultValue={articleData?.title} />
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Main Keyword</Label>
|
||||
<Textarea
|
||||
id="mainKeyword"
|
||||
value={articleData?.mainKeyword}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Additional Keyword</Label>
|
||||
<Textarea
|
||||
id="additionalKeywords"
|
||||
value={articleData?.additionalKeywords}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Title</Label>
|
||||
<Textarea id="metaTitle" value={articleData?.metaTitle} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Description</Label>
|
||||
<Textarea
|
||||
id="metaDescription"
|
||||
value={articleData?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<Label>Article</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="checker">
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-start justify-start">
|
||||
<Pie
|
||||
data={data}
|
||||
options={options}
|
||||
className="text-left flex items-start justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Accordion type="single" collapsible className="w-full ">
|
||||
<AccordionItem
|
||||
value="error"
|
||||
className="border border-red-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-red-600" />
|
||||
Errors ({errorSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{errorSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{errorSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No errors found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="warning"
|
||||
className="border border-yellow-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-yellow-600" />
|
||||
Warnings ({warningSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{warningSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{warningSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No warnings found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="optimized"
|
||||
className="border border-green-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-green-600" />
|
||||
Optimized ({optimizedSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{optimizedSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{optimizedSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No optimizations found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<Label>Article</Label>
|
||||
<Button size="md" className="bg-blue-500">
|
||||
Select Image From Content Bank
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ import { data } from "jquery";
|
|||
import { options } from "@fullcalendar/core/preact.js";
|
||||
import dynamic from "next/dynamic";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -939,18 +940,8 @@ export default function FormImage() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<a
|
||||
href={`/admin/media/${
|
||||
fileTypeId === "1"
|
||||
? "image"
|
||||
: fileTypeId === "2"
|
||||
? "video"
|
||||
: fileTypeId === "3"
|
||||
? "text"
|
||||
: "audio"
|
||||
}/update-new/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href={`/contributor/content/image/update-seo/${selectedArticleId}`}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
@ -960,7 +951,7 @@ export default function FormImage() {
|
|||
>
|
||||
Edit
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,757 @@
|
|||
"use client";
|
||||
import React, {
|
||||
ChangeEvent,
|
||||
Fragment,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
|
||||
import Cookies from "js-cookie";
|
||||
import {
|
||||
createMedia,
|
||||
deleteFile,
|
||||
deleteMedia,
|
||||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
uploadThumbnail,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import Image from "next/image";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Upload } from "tus-js-client";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import ViewEditor from "@/components/editor/view-editor";
|
||||
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
|
||||
import { Gauge } from "@mui/x-charts/Gauge";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { Pie } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ChartOptions,
|
||||
} from "chart.js";
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend);
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type Detail = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
category: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
publishedFor: string;
|
||||
|
||||
publishedForObject: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
htmlDescription: string;
|
||||
creatorName: string;
|
||||
categoryName: string;
|
||||
thumbnailLink: string;
|
||||
tags: string;
|
||||
};
|
||||
|
||||
type Option = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
}
|
||||
|
||||
export default function FormImageSeo() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [selectedOptions, setSelectedOptions] = useState<{
|
||||
[fileId: number]: string;
|
||||
}>({});
|
||||
const [articleData, setArticleData] = useState({
|
||||
title: "",
|
||||
mainKeyword: "",
|
||||
additionalKeywords: "",
|
||||
metaTitle: "",
|
||||
metaDescription: "",
|
||||
});
|
||||
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
|
||||
const [errorSEO, setErrorSEO] = useState<string[]>([]);
|
||||
const [warningSEO, setWarningSEO] = useState<string[]>([]);
|
||||
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
|
||||
// const [errorData, setErrorData] = useState<string[]>([]);
|
||||
const [warningData, setWarningData] = useState<string[]>([]);
|
||||
const [optimizedData, setOptimizedData] = useState<string[]>([]);
|
||||
const [errorsData, setErrorData] = useState<string[]>([]);
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
|
||||
detail?.category.id
|
||||
);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
let fileTypeId = "1";
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
});
|
||||
|
||||
// const handleKeyDown = (e: any) => {
|
||||
// const newTag = e.target.value.trim(); // Ambil nilai input
|
||||
// if (e.key === "Enter" && newTag) {
|
||||
// e.preventDefault(); // Hentikan submit form
|
||||
// if (!tags.includes(newTag)) {
|
||||
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
|
||||
// setValue("tags", ""); // Kosongkan input
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
const files = Array.from(event.target.files);
|
||||
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
|
||||
console.log("DATAFILE::", selectedFiles);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// const handleCheckboxChange = (id: number) => {
|
||||
// setSelectedPublishers((prev) =>
|
||||
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
// );
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getCategories();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const getCategories = async () => {
|
||||
try {
|
||||
const category = await listEnableCategory(fileTypeId);
|
||||
const resCategory: Category[] = category?.data?.data?.content;
|
||||
|
||||
setCategories(resCategory);
|
||||
console.log("data category", resCategory);
|
||||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
// setValue("categoryId", findCategory.id);
|
||||
setSelectedCategory(findCategory.id); // Set the selected category
|
||||
const response = await getTagsBySubCategoryId(findCategory.id);
|
||||
setTags(response?.data?.data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch categories:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchArticleData = async () => {
|
||||
try {
|
||||
const response = await getDetailArticle(id);
|
||||
const data = response?.data?.data;
|
||||
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
|
||||
|
||||
setArticleData({
|
||||
title: data.title || "",
|
||||
mainKeyword: data.mainKeyword || "",
|
||||
additionalKeywords: data.additionalKeywords || "",
|
||||
metaTitle: data.metaTitle || "",
|
||||
metaDescription: data.metaDescription || "",
|
||||
});
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
|
||||
// reset({
|
||||
// title: data.title,
|
||||
// mainKeyword: data.mainKeyword,
|
||||
// additionalKeywords: data.additionalKeywords,
|
||||
// metaTitle: data.metaTitle,
|
||||
// metaDescription: data.metaDescription,
|
||||
// });
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch article data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
if (id) {
|
||||
fetchArticleData();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSeoScore = async () => {
|
||||
const res = await getSeoScore(id);
|
||||
if (res.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
|
||||
let errorList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
|
||||
];
|
||||
setErrorSEO(errorList);
|
||||
let warningList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
|
||||
];
|
||||
setWarningSEO(warningList);
|
||||
let optimizedList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
|
||||
?.optimized,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
|
||||
];
|
||||
setOptimizedSEO(optimizedList);
|
||||
setErrorData(errorList);
|
||||
setWarningData(warningList);
|
||||
setOptimizedData(optimizedList);
|
||||
};
|
||||
fetchSeoScore();
|
||||
});
|
||||
|
||||
const data = {
|
||||
labels: ["SEO Score (" + totalScoreSEO + "%)"],
|
||||
datasets: [
|
||||
{
|
||||
data: [totalScoreSEO],
|
||||
backgroundColor: ["#4CAF50"],
|
||||
hoverBackgroundColor: ["#388E3C"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const options: ChartOptions<"pie"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "bottom", // TypeScript now correctly recognizes this as a valid option
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const requestData = {
|
||||
...data,
|
||||
id: detail?.id,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
htmlDescription: data.description,
|
||||
fileTypeId,
|
||||
categoryId: selectedTarget,
|
||||
subCategoryId: selectedTarget,
|
||||
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
|
||||
statusId: "1",
|
||||
publishedFor: publishedFor.join(","),
|
||||
creatorName: data.creatorName,
|
||||
tags: finalTags,
|
||||
isYoutube: false,
|
||||
isInternationalMedia: false,
|
||||
};
|
||||
|
||||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
const formMedia = new FormData();
|
||||
const thumbnail = files[0];
|
||||
formMedia.append("file", thumbnail);
|
||||
const responseThumbnail = await uploadThumbnail(id, formMedia);
|
||||
if (responseThumbnail?.error == true) {
|
||||
error(responseThumbnail?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const progressInfoArr = [];
|
||||
for (const item of files) {
|
||||
progressInfoArr.push({ percentage: 0, fileName: item.name });
|
||||
}
|
||||
progressInfo = progressInfoArr;
|
||||
setIsStartUpload(true);
|
||||
setProgressList(progressInfoArr);
|
||||
|
||||
close();
|
||||
// showProgress();
|
||||
files.map(async (item: any, index: number) => {
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
);
|
||||
});
|
||||
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
async function uploadResumableFile(
|
||||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
duration: string
|
||||
) {
|
||||
console.log(idx, id, file, duration);
|
||||
|
||||
// const placements = getPlacement(file.placements);
|
||||
// console.log("Placementttt: : ", placements);
|
||||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
metadata: {
|
||||
mediaid: id,
|
||||
filename: file.name,
|
||||
filetype: file.type,
|
||||
duration,
|
||||
isWatermark: "true", // hardcode
|
||||
},
|
||||
onBeforeRequest: function (req) {
|
||||
var xhr = req.getUnderlyingObject();
|
||||
xhr.withCredentials = true;
|
||||
},
|
||||
onError: async (e: any) => {
|
||||
console.log("Error upload :", e);
|
||||
error(e);
|
||||
},
|
||||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
) => {
|
||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
progressInfo[idx].percentage = uploadPersen;
|
||||
counterUpdateProgress++;
|
||||
console.log(counterUpdateProgress);
|
||||
setProgressList(progressInfo);
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
uploadPersen = 100;
|
||||
progressInfo[idx].percentage = 100;
|
||||
counterUpdateProgress++;
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
successTodo();
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
}
|
||||
|
||||
const onSubmit = (data: ImageSchema) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const successSubmit = (redirect: string) => {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
});
|
||||
};
|
||||
|
||||
function successTodo() {
|
||||
let counter = 0;
|
||||
for (const element of progressInfo) {
|
||||
if (element.percentage == 100) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
if (counter == progressInfo.length) {
|
||||
setIsStartUpload(false);
|
||||
// hideProgress();
|
||||
Cookies.remove("idCreate");
|
||||
successSubmit("/in/contributor/content/image/");
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveFile = (file: FileWithPreview) => {
|
||||
const uploadedFiles = files;
|
||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
setFiles([...filtered]);
|
||||
};
|
||||
|
||||
function success() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleDeleteFile = (id: number) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus file",
|
||||
text: "Apakah Anda yakin ingin menghapus file ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDelete(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async function doDelete(id: number) {
|
||||
const data = { id };
|
||||
|
||||
try {
|
||||
const response = await deleteFile(data);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Jika berhasil, hapus file dari state lokal
|
||||
setFiles((prevFiles: any) =>
|
||||
prevFiles.filter((file: any) => file.id !== id)
|
||||
);
|
||||
success();
|
||||
} catch (err) {
|
||||
error("Terjadi kesalahan saat menghapus file");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-5 mb-3">
|
||||
<Card>
|
||||
<Tabs defaultValue="content" className="">
|
||||
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Konten
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="checker"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Checker
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="content">
|
||||
{articleData !== undefined ? (
|
||||
<CardContent className="space-y-2 my-3">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="name">Judul</Label>
|
||||
<Input id="name" defaultValue={articleData?.title} />
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Main Keyword</Label>
|
||||
<Textarea
|
||||
id="mainKeyword"
|
||||
value={articleData?.mainKeyword}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Additional Keyword</Label>
|
||||
<Textarea
|
||||
id="additionalKeywords"
|
||||
value={articleData?.additionalKeywords}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Title</Label>
|
||||
<Textarea id="metaTitle" value={articleData?.metaTitle} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Description</Label>
|
||||
<Textarea
|
||||
id="metaDescription"
|
||||
value={articleData?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<Label>Article</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="checker">
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-start justify-start">
|
||||
<Pie
|
||||
data={data}
|
||||
options={options}
|
||||
className="text-left flex items-start justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Accordion type="single" collapsible className="w-full ">
|
||||
<AccordionItem
|
||||
value="error"
|
||||
className="border border-red-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-red-600" />
|
||||
Errors ({errorSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{errorSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{errorSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No errors found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="warning"
|
||||
className="border border-yellow-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-yellow-600" />
|
||||
Warnings ({warningSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{warningSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{warningSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No warnings found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="optimized"
|
||||
className="border border-green-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-green-600" />
|
||||
Optimized ({optimizedSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{optimizedSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{optimizedSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No optimizations found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<Label>Article</Label>
|
||||
<Button size="md" className="bg-blue-500">
|
||||
Select Image From Content Bank
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import Cookies from "js-cookie";
|
|||
import {
|
||||
convertSPIT,
|
||||
createMedia,
|
||||
deleteSPIT,
|
||||
detailSPIT,
|
||||
getTagsBySubCategoryId,
|
||||
listCategory,
|
||||
|
|
@ -47,6 +48,7 @@ import { request } from "http";
|
|||
import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import dynamic from "next/dynamic";
|
||||
import { error } from "@/lib/swal";
|
||||
|
||||
const imageSchema = z.object({
|
||||
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -538,6 +540,49 @@ export default function FormConvertSPIT() {
|
|||
}
|
||||
};
|
||||
|
||||
function deleteSpitContent() {
|
||||
MySwal.fire({
|
||||
title: "Apakah anda ingin menghapus konten?",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#dc3545",
|
||||
confirmButtonText: "Iya",
|
||||
cancelButtonText: "Tidak",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDeleteSPIT();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function doDeleteSPIT() {
|
||||
const response = await deleteSPIT(id);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
successBack();
|
||||
}
|
||||
|
||||
function successBack() {
|
||||
MySwal?.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
if (window.history.state && window.history.state.idx > 0) {
|
||||
console.log("backkkkk");
|
||||
console.log(window.history.state);
|
||||
router.back();
|
||||
} else {
|
||||
router.push("/in/contributor/content/spit");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{detail !== undefined ? (
|
||||
|
|
@ -922,8 +967,12 @@ export default function FormConvertSPIT() {
|
|||
</Button>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Button type="submit" color="primary" variant="outline">
|
||||
Cancel
|
||||
<Button
|
||||
type="submit"
|
||||
className="bg-red-500 hover:bg-red-700"
|
||||
onClick={() => deleteSpitContent()}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
|
|||
import { Item } from "@radix-ui/react-dropdown-menu";
|
||||
import dynamic from "next/dynamic";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -919,7 +920,7 @@ export default function FormTeks() {
|
|||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{id}
|
||||
{"Narasi " + (index + 1)}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -928,18 +929,8 @@ export default function FormTeks() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<a
|
||||
href={`/admin/media/${
|
||||
fileTypeId === "1"
|
||||
? "image"
|
||||
: fileTypeId === "2"
|
||||
? "video"
|
||||
: fileTypeId === "3"
|
||||
? "text"
|
||||
: "audio"
|
||||
}/update-new/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href={`/contributor/content/teks/update-seo/${selectedArticleId}`}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
@ -949,7 +940,7 @@ export default function FormTeks() {
|
|||
>
|
||||
Edit
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,757 @@
|
|||
"use client";
|
||||
import React, {
|
||||
ChangeEvent,
|
||||
Fragment,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
|
||||
import Cookies from "js-cookie";
|
||||
import {
|
||||
createMedia,
|
||||
deleteFile,
|
||||
deleteMedia,
|
||||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
uploadThumbnail,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import Image from "next/image";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Upload } from "tus-js-client";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import ViewEditor from "@/components/editor/view-editor";
|
||||
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
|
||||
import { Gauge } from "@mui/x-charts/Gauge";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { Pie } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ChartOptions,
|
||||
} from "chart.js";
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend);
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type Detail = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
category: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
publishedFor: string;
|
||||
|
||||
publishedForObject: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
htmlDescription: string;
|
||||
creatorName: string;
|
||||
categoryName: string;
|
||||
thumbnailLink: string;
|
||||
tags: string;
|
||||
};
|
||||
|
||||
type Option = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
}
|
||||
|
||||
export default function FormTeksSeo() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [selectedOptions, setSelectedOptions] = useState<{
|
||||
[fileId: number]: string;
|
||||
}>({});
|
||||
const [articleData, setArticleData] = useState({
|
||||
title: "",
|
||||
mainKeyword: "",
|
||||
additionalKeywords: "",
|
||||
metaTitle: "",
|
||||
metaDescription: "",
|
||||
});
|
||||
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
|
||||
const [errorSEO, setErrorSEO] = useState<string[]>([]);
|
||||
const [warningSEO, setWarningSEO] = useState<string[]>([]);
|
||||
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
|
||||
// const [errorData, setErrorData] = useState<string[]>([]);
|
||||
const [warningData, setWarningData] = useState<string[]>([]);
|
||||
const [optimizedData, setOptimizedData] = useState<string[]>([]);
|
||||
const [errorsData, setErrorData] = useState<string[]>([]);
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
|
||||
detail?.category.id
|
||||
);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
let fileTypeId = "1";
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
});
|
||||
|
||||
// const handleKeyDown = (e: any) => {
|
||||
// const newTag = e.target.value.trim(); // Ambil nilai input
|
||||
// if (e.key === "Enter" && newTag) {
|
||||
// e.preventDefault(); // Hentikan submit form
|
||||
// if (!tags.includes(newTag)) {
|
||||
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
|
||||
// setValue("tags", ""); // Kosongkan input
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
const files = Array.from(event.target.files);
|
||||
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
|
||||
console.log("DATAFILE::", selectedFiles);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// const handleCheckboxChange = (id: number) => {
|
||||
// setSelectedPublishers((prev) =>
|
||||
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
// );
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getCategories();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const getCategories = async () => {
|
||||
try {
|
||||
const category = await listEnableCategory(fileTypeId);
|
||||
const resCategory: Category[] = category?.data?.data?.content;
|
||||
|
||||
setCategories(resCategory);
|
||||
console.log("data category", resCategory);
|
||||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
// setValue("categoryId", findCategory.id);
|
||||
setSelectedCategory(findCategory.id); // Set the selected category
|
||||
const response = await getTagsBySubCategoryId(findCategory.id);
|
||||
setTags(response?.data?.data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch categories:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchArticleData = async () => {
|
||||
try {
|
||||
const response = await getDetailArticle(id);
|
||||
const data = response?.data?.data;
|
||||
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
|
||||
|
||||
setArticleData({
|
||||
title: data.title || "",
|
||||
mainKeyword: data.mainKeyword || "",
|
||||
additionalKeywords: data.additionalKeywords || "",
|
||||
metaTitle: data.metaTitle || "",
|
||||
metaDescription: data.metaDescription || "",
|
||||
});
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
|
||||
// reset({
|
||||
// title: data.title,
|
||||
// mainKeyword: data.mainKeyword,
|
||||
// additionalKeywords: data.additionalKeywords,
|
||||
// metaTitle: data.metaTitle,
|
||||
// metaDescription: data.metaDescription,
|
||||
// });
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch article data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
if (id) {
|
||||
fetchArticleData();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSeoScore = async () => {
|
||||
const res = await getSeoScore(id);
|
||||
if (res.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
|
||||
let errorList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
|
||||
];
|
||||
setErrorSEO(errorList);
|
||||
let warningList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
|
||||
];
|
||||
setWarningSEO(warningList);
|
||||
let optimizedList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
|
||||
?.optimized,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
|
||||
];
|
||||
setOptimizedSEO(optimizedList);
|
||||
setErrorData(errorList);
|
||||
setWarningData(warningList);
|
||||
setOptimizedData(optimizedList);
|
||||
};
|
||||
fetchSeoScore();
|
||||
});
|
||||
|
||||
const data = {
|
||||
labels: ["SEO Score (" + totalScoreSEO + "%)"],
|
||||
datasets: [
|
||||
{
|
||||
data: [totalScoreSEO],
|
||||
backgroundColor: ["#4CAF50"],
|
||||
hoverBackgroundColor: ["#388E3C"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const options: ChartOptions<"pie"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "bottom", // TypeScript now correctly recognizes this as a valid option
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const requestData = {
|
||||
...data,
|
||||
id: detail?.id,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
htmlDescription: data.description,
|
||||
fileTypeId,
|
||||
categoryId: selectedTarget,
|
||||
subCategoryId: selectedTarget,
|
||||
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
|
||||
statusId: "1",
|
||||
publishedFor: publishedFor.join(","),
|
||||
creatorName: data.creatorName,
|
||||
tags: finalTags,
|
||||
isYoutube: false,
|
||||
isInternationalMedia: false,
|
||||
};
|
||||
|
||||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
const formMedia = new FormData();
|
||||
const thumbnail = files[0];
|
||||
formMedia.append("file", thumbnail);
|
||||
const responseThumbnail = await uploadThumbnail(id, formMedia);
|
||||
if (responseThumbnail?.error == true) {
|
||||
error(responseThumbnail?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const progressInfoArr = [];
|
||||
for (const item of files) {
|
||||
progressInfoArr.push({ percentage: 0, fileName: item.name });
|
||||
}
|
||||
progressInfo = progressInfoArr;
|
||||
setIsStartUpload(true);
|
||||
setProgressList(progressInfoArr);
|
||||
|
||||
close();
|
||||
// showProgress();
|
||||
files.map(async (item: any, index: number) => {
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
);
|
||||
});
|
||||
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
async function uploadResumableFile(
|
||||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
duration: string
|
||||
) {
|
||||
console.log(idx, id, file, duration);
|
||||
|
||||
// const placements = getPlacement(file.placements);
|
||||
// console.log("Placementttt: : ", placements);
|
||||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
metadata: {
|
||||
mediaid: id,
|
||||
filename: file.name,
|
||||
filetype: file.type,
|
||||
duration,
|
||||
isWatermark: "true", // hardcode
|
||||
},
|
||||
onBeforeRequest: function (req) {
|
||||
var xhr = req.getUnderlyingObject();
|
||||
xhr.withCredentials = true;
|
||||
},
|
||||
onError: async (e: any) => {
|
||||
console.log("Error upload :", e);
|
||||
error(e);
|
||||
},
|
||||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
) => {
|
||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
progressInfo[idx].percentage = uploadPersen;
|
||||
counterUpdateProgress++;
|
||||
console.log(counterUpdateProgress);
|
||||
setProgressList(progressInfo);
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
uploadPersen = 100;
|
||||
progressInfo[idx].percentage = 100;
|
||||
counterUpdateProgress++;
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
successTodo();
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
}
|
||||
|
||||
const onSubmit = (data: ImageSchema) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const successSubmit = (redirect: string) => {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
});
|
||||
};
|
||||
|
||||
function successTodo() {
|
||||
let counter = 0;
|
||||
for (const element of progressInfo) {
|
||||
if (element.percentage == 100) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
if (counter == progressInfo.length) {
|
||||
setIsStartUpload(false);
|
||||
// hideProgress();
|
||||
Cookies.remove("idCreate");
|
||||
successSubmit("/in/contributor/content/image/");
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveFile = (file: FileWithPreview) => {
|
||||
const uploadedFiles = files;
|
||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
setFiles([...filtered]);
|
||||
};
|
||||
|
||||
function success() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleDeleteFile = (id: number) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus file",
|
||||
text: "Apakah Anda yakin ingin menghapus file ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDelete(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async function doDelete(id: number) {
|
||||
const data = { id };
|
||||
|
||||
try {
|
||||
const response = await deleteFile(data);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Jika berhasil, hapus file dari state lokal
|
||||
setFiles((prevFiles: any) =>
|
||||
prevFiles.filter((file: any) => file.id !== id)
|
||||
);
|
||||
success();
|
||||
} catch (err) {
|
||||
error("Terjadi kesalahan saat menghapus file");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-5 mb-3">
|
||||
<Card>
|
||||
<Tabs defaultValue="content" className="">
|
||||
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Konten
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="checker"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Checker
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="content">
|
||||
{articleData !== undefined ? (
|
||||
<CardContent className="space-y-2 my-3">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="name">Judul</Label>
|
||||
<Input id="name" defaultValue={articleData?.title} />
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Main Keyword</Label>
|
||||
<Textarea
|
||||
id="mainKeyword"
|
||||
value={articleData?.mainKeyword}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Additional Keyword</Label>
|
||||
<Textarea
|
||||
id="additionalKeywords"
|
||||
value={articleData?.additionalKeywords}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Title</Label>
|
||||
<Textarea id="metaTitle" value={articleData?.metaTitle} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Description</Label>
|
||||
<Textarea
|
||||
id="metaDescription"
|
||||
value={articleData?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<Label>Article</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="checker">
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-start justify-start">
|
||||
<Pie
|
||||
data={data}
|
||||
options={options}
|
||||
className="text-left flex items-start justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Accordion type="single" collapsible className="w-full ">
|
||||
<AccordionItem
|
||||
value="error"
|
||||
className="border border-red-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-red-600" />
|
||||
Errors ({errorSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{errorSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{errorSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No errors found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="warning"
|
||||
className="border border-yellow-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-yellow-600" />
|
||||
Warnings ({warningSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{warningSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{warningSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No warnings found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="optimized"
|
||||
className="border border-green-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-green-600" />
|
||||
Optimized ({optimizedSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{optimizedSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{optimizedSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No optimizations found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<Label>Article</Label>
|
||||
<Button size="md" className="bg-blue-500">
|
||||
Select Image From Content Bank
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
|
|||
import { Item } from "@radix-ui/react-dropdown-menu";
|
||||
import dynamic from "next/dynamic";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
|
|
@ -917,7 +918,7 @@ export default function FormVideo() {
|
|||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{id}
|
||||
{"Narasi " + (index + 1)}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -926,18 +927,8 @@ export default function FormVideo() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<a
|
||||
href={`/admin/media/${
|
||||
fileTypeId === "1"
|
||||
? "image"
|
||||
: fileTypeId === "2"
|
||||
? "video"
|
||||
: fileTypeId === "3"
|
||||
? "text"
|
||||
: "audio"
|
||||
}/update-new/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href={`/contributor/content/video/update-seo/${selectedArticleId}`}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
|
|
@ -947,7 +938,7 @@ export default function FormVideo() {
|
|||
>
|
||||
Edit
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,757 @@
|
|||
"use client";
|
||||
import React, {
|
||||
ChangeEvent,
|
||||
Fragment,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
|
||||
import Cookies from "js-cookie";
|
||||
import {
|
||||
createMedia,
|
||||
deleteFile,
|
||||
deleteMedia,
|
||||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
uploadThumbnail,
|
||||
} from "@/service/content/content";
|
||||
import { detailMedia } from "@/service/curated-content/curated-content";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import Image from "next/image";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Upload } from "tus-js-client";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import ViewEditor from "@/components/editor/view-editor";
|
||||
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
|
||||
import { Gauge } from "@mui/x-charts/Gauge";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { Pie } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ChartOptions,
|
||||
} from "chart.js";
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend);
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type Detail = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
category: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
publishedFor: string;
|
||||
|
||||
publishedForObject: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
htmlDescription: string;
|
||||
creatorName: string;
|
||||
categoryName: string;
|
||||
thumbnailLink: string;
|
||||
tags: string;
|
||||
};
|
||||
|
||||
type Option = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
}
|
||||
|
||||
export default function FormVideoSeo() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const { id } = useParams() as { id: string };
|
||||
console.log(id);
|
||||
const editor = useRef(null);
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
let progressInfo: any = [];
|
||||
let counterUpdateProgress = 0;
|
||||
const [progressList, setProgressList] = useState<any>([]);
|
||||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [selectedOptions, setSelectedOptions] = useState<{
|
||||
[fileId: number]: string;
|
||||
}>({});
|
||||
const [articleData, setArticleData] = useState({
|
||||
title: "",
|
||||
mainKeyword: "",
|
||||
additionalKeywords: "",
|
||||
metaTitle: "",
|
||||
metaDescription: "",
|
||||
});
|
||||
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
|
||||
const [errorSEO, setErrorSEO] = useState<string[]>([]);
|
||||
const [warningSEO, setWarningSEO] = useState<string[]>([]);
|
||||
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
|
||||
// const [errorData, setErrorData] = useState<string[]>([]);
|
||||
const [warningData, setWarningData] = useState<string[]>([]);
|
||||
const [optimizedData, setOptimizedData] = useState<string[]>([]);
|
||||
const [errorsData, setErrorData] = useState<string[]>([]);
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
|
||||
detail?.category.id
|
||||
);
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
let fileTypeId = "1";
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
});
|
||||
|
||||
// const handleKeyDown = (e: any) => {
|
||||
// const newTag = e.target.value.trim(); // Ambil nilai input
|
||||
// if (e.key === "Enter" && newTag) {
|
||||
// e.preventDefault(); // Hentikan submit form
|
||||
// if (!tags.includes(newTag)) {
|
||||
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
|
||||
// setValue("tags", ""); // Kosongkan input
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
const files = Array.from(event.target.files);
|
||||
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
|
||||
console.log("DATAFILE::", selectedFiles);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// const handleCheckboxChange = (id: number) => {
|
||||
// setSelectedPublishers((prev) =>
|
||||
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||
// );
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getCategories();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
const getCategories = async () => {
|
||||
try {
|
||||
const category = await listEnableCategory(fileTypeId);
|
||||
const resCategory: Category[] = category?.data?.data?.content;
|
||||
|
||||
setCategories(resCategory);
|
||||
console.log("data category", resCategory);
|
||||
|
||||
if (scheduleId && scheduleType === "3") {
|
||||
const findCategory = resCategory.find((o) =>
|
||||
o.name.toLowerCase().includes("pers rilis")
|
||||
);
|
||||
|
||||
if (findCategory) {
|
||||
// setValue("categoryId", findCategory.id);
|
||||
setSelectedCategory(findCategory.id); // Set the selected category
|
||||
const response = await getTagsBySubCategoryId(findCategory.id);
|
||||
setTags(response?.data?.data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch categories:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchArticleData = async () => {
|
||||
try {
|
||||
const response = await getDetailArticle(id);
|
||||
const data = response?.data?.data;
|
||||
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
|
||||
|
||||
setArticleData({
|
||||
title: data.title || "",
|
||||
mainKeyword: data.mainKeyword || "",
|
||||
additionalKeywords: data.additionalKeywords || "",
|
||||
metaTitle: data.metaTitle || "",
|
||||
metaDescription: data.metaDescription || "",
|
||||
});
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
|
||||
// reset({
|
||||
// title: data.title,
|
||||
// mainKeyword: data.mainKeyword,
|
||||
// additionalKeywords: data.additionalKeywords,
|
||||
// metaTitle: data.metaTitle,
|
||||
// metaDescription: data.metaDescription,
|
||||
// });
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch article data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
if (id) {
|
||||
fetchArticleData();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSeoScore = async () => {
|
||||
const res = await getSeoScore(id);
|
||||
if (res.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
|
||||
let errorList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
|
||||
];
|
||||
setErrorSEO(errorList);
|
||||
let warningList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
|
||||
];
|
||||
setWarningSEO(warningList);
|
||||
let optimizedList = [
|
||||
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
|
||||
?.optimized,
|
||||
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
|
||||
];
|
||||
setOptimizedSEO(optimizedList);
|
||||
setErrorData(errorList);
|
||||
setWarningData(warningList);
|
||||
setOptimizedData(optimizedList);
|
||||
};
|
||||
fetchSeoScore();
|
||||
});
|
||||
|
||||
const data = {
|
||||
labels: ["SEO Score (" + totalScoreSEO + "%)"],
|
||||
datasets: [
|
||||
{
|
||||
data: [totalScoreSEO],
|
||||
backgroundColor: ["#4CAF50"],
|
||||
hoverBackgroundColor: ["#388E3C"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const options: ChartOptions<"pie"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "bottom", // TypeScript now correctly recognizes this as a valid option
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const requestData = {
|
||||
...data,
|
||||
id: detail?.id,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
htmlDescription: data.description,
|
||||
fileTypeId,
|
||||
categoryId: selectedTarget,
|
||||
subCategoryId: selectedTarget,
|
||||
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
|
||||
statusId: "1",
|
||||
publishedFor: publishedFor.join(","),
|
||||
creatorName: data.creatorName,
|
||||
tags: finalTags,
|
||||
isYoutube: false,
|
||||
isInternationalMedia: false,
|
||||
};
|
||||
|
||||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
const formMedia = new FormData();
|
||||
const thumbnail = files[0];
|
||||
formMedia.append("file", thumbnail);
|
||||
const responseThumbnail = await uploadThumbnail(id, formMedia);
|
||||
if (responseThumbnail?.error == true) {
|
||||
error(responseThumbnail?.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const progressInfoArr = [];
|
||||
for (const item of files) {
|
||||
progressInfoArr.push({ percentage: 0, fileName: item.name });
|
||||
}
|
||||
progressInfo = progressInfoArr;
|
||||
setIsStartUpload(true);
|
||||
setProgressList(progressInfoArr);
|
||||
|
||||
close();
|
||||
// showProgress();
|
||||
files.map(async (item: any, index: number) => {
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
);
|
||||
});
|
||||
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push("/en/contributor/content/image");
|
||||
});
|
||||
};
|
||||
|
||||
async function uploadResumableFile(
|
||||
idx: number,
|
||||
id: string,
|
||||
file: any,
|
||||
duration: string
|
||||
) {
|
||||
console.log(idx, id, file, duration);
|
||||
|
||||
// const placements = getPlacement(file.placements);
|
||||
// console.log("Placementttt: : ", placements);
|
||||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
|
||||
headers: headers,
|
||||
retryDelays: [0, 3000, 6000, 12_000, 24_000],
|
||||
chunkSize: 20_000,
|
||||
metadata: {
|
||||
mediaid: id,
|
||||
filename: file.name,
|
||||
filetype: file.type,
|
||||
duration,
|
||||
isWatermark: "true", // hardcode
|
||||
},
|
||||
onBeforeRequest: function (req) {
|
||||
var xhr = req.getUnderlyingObject();
|
||||
xhr.withCredentials = true;
|
||||
},
|
||||
onError: async (e: any) => {
|
||||
console.log("Error upload :", e);
|
||||
error(e);
|
||||
},
|
||||
onChunkComplete: (
|
||||
chunkSize: any,
|
||||
bytesAccepted: any,
|
||||
bytesTotal: any
|
||||
) => {
|
||||
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
|
||||
progressInfo[idx].percentage = uploadPersen;
|
||||
counterUpdateProgress++;
|
||||
console.log(counterUpdateProgress);
|
||||
setProgressList(progressInfo);
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
uploadPersen = 100;
|
||||
progressInfo[idx].percentage = 100;
|
||||
counterUpdateProgress++;
|
||||
setCounterProgress(counterUpdateProgress);
|
||||
successTodo();
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
}
|
||||
|
||||
const onSubmit = (data: ImageSchema) => {
|
||||
MySwal.fire({
|
||||
title: "Simpan Data",
|
||||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const successSubmit = (redirect: string) => {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Data berhasil disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
});
|
||||
};
|
||||
|
||||
function successTodo() {
|
||||
let counter = 0;
|
||||
for (const element of progressInfo) {
|
||||
if (element.percentage == 100) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
if (counter == progressInfo.length) {
|
||||
setIsStartUpload(false);
|
||||
// hideProgress();
|
||||
Cookies.remove("idCreate");
|
||||
successSubmit("/in/contributor/content/image/");
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveFile = (file: FileWithPreview) => {
|
||||
const uploadedFiles = files;
|
||||
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
|
||||
setFiles([...filtered]);
|
||||
};
|
||||
|
||||
function success() {
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleDeleteFile = (id: number) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus file",
|
||||
text: "Apakah Anda yakin ingin menghapus file ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDelete(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async function doDelete(id: number) {
|
||||
const data = { id };
|
||||
|
||||
try {
|
||||
const response = await deleteFile(data);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Jika berhasil, hapus file dari state lokal
|
||||
setFiles((prevFiles: any) =>
|
||||
prevFiles.filter((file: any) => file.id !== id)
|
||||
);
|
||||
success();
|
||||
} catch (err) {
|
||||
error("Terjadi kesalahan saat menghapus file");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-5 mb-3">
|
||||
<Card>
|
||||
<Tabs defaultValue="content" className="">
|
||||
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Konten
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="checker"
|
||||
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
|
||||
>
|
||||
Checker
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="content">
|
||||
{articleData !== undefined ? (
|
||||
<CardContent className="space-y-2 my-3">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="name">Judul</Label>
|
||||
<Input id="name" defaultValue={articleData?.title} />
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Main Keyword</Label>
|
||||
<Textarea
|
||||
id="mainKeyword"
|
||||
value={articleData?.mainKeyword}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Additional Keyword</Label>
|
||||
<Textarea
|
||||
id="additionalKeywords"
|
||||
value={articleData?.additionalKeywords}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3 w-full">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Title</Label>
|
||||
<Textarea id="metaTitle" value={articleData?.metaTitle} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="username">Meta Description</Label>
|
||||
<Textarea
|
||||
id="metaDescription"
|
||||
value={articleData?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<Label>Article</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="checker">
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-start justify-start">
|
||||
<Pie
|
||||
data={data}
|
||||
options={options}
|
||||
className="text-left flex items-start justify-start"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Accordion type="single" collapsible className="w-full ">
|
||||
<AccordionItem
|
||||
value="error"
|
||||
className="border border-red-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-red-600" />
|
||||
Errors ({errorSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{errorSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{errorSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No errors found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="warning"
|
||||
className="border border-yellow-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-yellow-600" />
|
||||
Warnings ({warningSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{warningSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{warningSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No warnings found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem
|
||||
value="optimized"
|
||||
className="border border-green-600"
|
||||
>
|
||||
<AccordionTrigger>
|
||||
<div className="flex items-center">
|
||||
<XIcon className="text-green-600" />
|
||||
Optimized ({optimizedSEO.length})
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{optimizedSEO.length > 0 ? (
|
||||
<ul className="list-disc list-inside">
|
||||
{optimizedSEO.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No optimizations found.</p>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="py-3">
|
||||
<div className="flex flex-row justify-between items-center mb-3">
|
||||
<Label>Article</Label>
|
||||
<Button size="md" className="bg-blue-500">
|
||||
Select Image From Content Bank
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ export default function FormTaskDetail() {
|
|||
|
||||
const userLevelNumber = getCookiesDecrypt("ulne");
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const userLevelId = "";
|
||||
|
||||
// State for various form fields
|
||||
const [taskOutput, setTaskOutput] = useState({
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -24,6 +24,7 @@
|
|||
"@fullcalendar/timegrid": "^6.1.15",
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@iconify/react": "^5.0.2",
|
||||
"@mui/x-charts": "^7.25.0",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-aspect-ratio": "^1.0.3",
|
||||
|
|
@ -68,8 +69,10 @@
|
|||
"@types/sanitize-html": "^2.13.0",
|
||||
"@vercel/analytics": "^1.3.1",
|
||||
"@wavesurfer/react": "^1.0.8",
|
||||
"add": "^2.0.6",
|
||||
"apexcharts": "^3.49.2",
|
||||
"axios": "^1.7.8",
|
||||
"chart": "^0.1.2",
|
||||
"chart.js": "^4.4.3",
|
||||
"ckeditor5-custom-build": "file:vendor/ckeditor5",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
|
|
@ -133,6 +136,7 @@
|
|||
"recharts": "^2.12.7",
|
||||
"rtl-detect": "^1.1.2",
|
||||
"sanitize-html": "^2.14.0",
|
||||
"shadcn": "^2.3.0",
|
||||
"sharp": "^0.33.4",
|
||||
"sonner": "^1.5.0",
|
||||
"sweetalert2": "^11.10.5",
|
||||
|
|
|
|||
|
|
@ -214,3 +214,8 @@ export async function deleteFile(data: any) {
|
|||
const url = "media/file";
|
||||
return httpDeleteInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function deleteSPIT(id: any) {
|
||||
const url = `media/spit?id=${id}`;
|
||||
return httpDeleteInterceptor(url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,15 @@ export async function listTask(
|
|||
size: any,
|
||||
code: any,
|
||||
createdAt: any,
|
||||
taskType: string
|
||||
taskType: string,
|
||||
status: number[]
|
||||
) {
|
||||
const statusQuery = status.length
|
||||
? `&isDone=${status.includes(1)}&isActive=${status.includes(2)}`
|
||||
: "";
|
||||
|
||||
return httpGetInterceptor(
|
||||
`assignment/list?enablePage=1&size=${size}&page=${page}&title=${title}&taskType=${taskType}&uniqueCode=${code}&createdAt=${createdAt}`
|
||||
`assignment/list?enablePage=1&size=${size}&page=${page}&title=${title}&taskType=${taskType}&uniqueCode=${code}&createdAt=${createdAt}${statusQuery}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,162 +0,0 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
All changes in the package are documented in the main repository. See: https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md.
|
||||
|
||||
Changes for the past releases are available below.
|
||||
|
||||
## [19.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v18.0.0...v19.0.0) (April 29, 2020)
|
||||
|
||||
Internal changes only (updated dependencies, documentation, etc.).
|
||||
|
||||
|
||||
## [18.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v17.0.0...v18.0.0) (March 19, 2020)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([f1beaaa](https://github.com/ckeditor/ckeditor5-alignment/commit/f1beaaa))
|
||||
|
||||
|
||||
## [17.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v16.0.0...v17.0.0) (February 18, 2020)
|
||||
|
||||
### MAJOR BREAKING CHANGES
|
||||
|
||||
* The `align-left`, `align-right`, `align-center`, and `align-justify` icons have been moved to `@ckeditor/ckeditor5-core`.
|
||||
|
||||
### Other changes
|
||||
|
||||
* Moved alignment icons to `@ckeditor/ckeditor5-core` (see [ckeditor/ckeditor5-table#227](https://github.com/ckeditor/ckeditor5-table/issues/227)). ([410e279](https://github.com/ckeditor/ckeditor5-alignment/commit/410e279))
|
||||
* Updated translations. ([288672f](https://github.com/ckeditor/ckeditor5-alignment/commit/288672f))
|
||||
|
||||
|
||||
## [16.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v15.0.0...v16.0.0) (December 4, 2019)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([9085f7b](https://github.com/ckeditor/ckeditor5-alignment/commit/9085f7b))
|
||||
|
||||
|
||||
## [15.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v11.2.0...v15.0.0) (October 23, 2019)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([a719974](https://github.com/ckeditor/ckeditor5-alignment/commit/a719974)) ([2fed077](https://github.com/ckeditor/ckeditor5-alignment/commit/2fed077))
|
||||
* Added `pluginName` to the editor plugin part of the feature. ([3b42798](https://github.com/ckeditor/ckeditor5-alignment/commit/3b42798))
|
||||
|
||||
|
||||
## [11.2.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v11.1.3...v11.2.0) (August 26, 2019)
|
||||
|
||||
### Features
|
||||
|
||||
* Integrated the text alignment feature with different editor content directions (LTR and RTL). See [ckeditor/ckeditor5#1151](https://github.com/ckeditor/ckeditor5/issues/1151). ([edc7d8b](https://github.com/ckeditor/ckeditor5-alignment/commit/edc7d8b))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* The UI buttons should be marked as toggleable for better assistive technologies support (see [ckeditor/ckeditor5#1403](https://github.com/ckeditor/ckeditor5/issues/1403)). ([599ea01](https://github.com/ckeditor/ckeditor5-alignment/commit/599ea01))
|
||||
|
||||
### Other changes
|
||||
|
||||
* The issue tracker for this package was moved to https://github.com/ckeditor/ckeditor5/issues. See [ckeditor/ckeditor5#1988](https://github.com/ckeditor/ckeditor5/issues/1988). ([54f81b3](https://github.com/ckeditor/ckeditor5-alignment/commit/54f81b3))
|
||||
* The text alignment toolbar should have a proper `aria-label` attribute (see [ckeditor/ckeditor5#1404](https://github.com/ckeditor/ckeditor5/issues/1404)). ([3ed81de](https://github.com/ckeditor/ckeditor5-alignment/commit/3ed81de))
|
||||
* Updated translations. ([feb4ab3](https://github.com/ckeditor/ckeditor5-alignment/commit/feb4ab3))
|
||||
|
||||
|
||||
## [11.1.3](https://github.com/ckeditor/ckeditor5-alignment/compare/v11.1.2...v11.1.3) (July 10, 2019)
|
||||
|
||||
Internal changes only (updated dependencies, documentation, etc.).
|
||||
|
||||
|
||||
## [11.1.2](https://github.com/ckeditor/ckeditor5-alignment/compare/v11.1.1...v11.1.2) (July 4, 2019)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([bb7f494](https://github.com/ckeditor/ckeditor5-alignment/commit/bb7f494))
|
||||
|
||||
|
||||
## [11.1.1](https://github.com/ckeditor/ckeditor5-alignment/compare/v11.1.0...v11.1.1) (June 6, 2019)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([32c32c1](https://github.com/ckeditor/ckeditor5-alignment/commit/32c32c1))
|
||||
|
||||
|
||||
## [11.1.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v11.0.0...v11.1.0) (April 4, 2019)
|
||||
|
||||
### Features
|
||||
|
||||
* Marked alignment as a formatting attribute using the `AttributeProperties#isFormatting` property. Closes [ckeditor/ckeditor5#1664](https://github.com/ckeditor/ckeditor5/issues/1664). ([6358e08](https://github.com/ckeditor/ckeditor5-alignment/commit/6358e08))
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([78bfc40](https://github.com/ckeditor/ckeditor5-alignment/commit/78bfc40))
|
||||
|
||||
|
||||
## [11.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v10.0.4...v11.0.0) (February 28, 2019)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([45e8dd5](https://github.com/ckeditor/ckeditor5-alignment/commit/45e8dd5)) ([a92c37b](https://github.com/ckeditor/ckeditor5-alignment/commit/a92c37b)) ([ef68e54](https://github.com/ckeditor/ckeditor5-alignment/commit/ef68e54))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Upgraded minimal versions of Node to `8.0.0` and npm to `5.7.1`. See: [ckeditor/ckeditor5#1507](https://github.com/ckeditor/ckeditor5/issues/1507). ([612ea3c](https://github.com/ckeditor/ckeditor5-cloud-services/commit/612ea3c))
|
||||
|
||||
|
||||
## [10.0.4](https://github.com/ckeditor/ckeditor5-alignment/compare/v10.0.3...v10.0.4) (December 5, 2018)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Improved SVG icons size. See [ckeditor/ckeditor5-theme-lark#206](https://github.com/ckeditor/ckeditor5-theme-lark/issues/206). ([1d71d33](https://github.com/ckeditor/ckeditor5-alignment/commit/1d71d33))
|
||||
* Updated translations. ([547f8d8](https://github.com/ckeditor/ckeditor5-alignment/commit/547f8d8)) ([43d8225](https://github.com/ckeditor/ckeditor5-alignment/commit/43d8225))
|
||||
|
||||
|
||||
## [10.0.3](https://github.com/ckeditor/ckeditor5-alignment/compare/v10.0.2...v10.0.3) (October 8, 2018)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([5b30202](https://github.com/ckeditor/ckeditor5-alignment/commit/5b30202))
|
||||
|
||||
|
||||
## [10.0.2](https://github.com/ckeditor/ckeditor5-alignment/compare/v10.0.1...v10.0.2) (July 18, 2018)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([33c281c](https://github.com/ckeditor/ckeditor5-alignment/commit/33c281c))
|
||||
|
||||
|
||||
## [10.0.1](https://github.com/ckeditor/ckeditor5-alignment/compare/v10.0.0...v10.0.1) (June 21, 2018)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations.
|
||||
|
||||
|
||||
## [10.0.0](https://github.com/ckeditor/ckeditor5-alignment/compare/v1.0.0-beta.4...v10.0.0) (April 25, 2018)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Changed the license to GPL2+ only. See [ckeditor/ckeditor5#991](https://github.com/ckeditor/ckeditor5/issues/991). ([eed1029](https://github.com/ckeditor/ckeditor5-alignment/commit/eed1029))
|
||||
* Updated translations. ([baa1fbe](https://github.com/ckeditor/ckeditor5-alignment/commit/baa1fbe))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* The license under which CKEditor 5 is released has been changed from a triple GPL, LGPL and MPL license to a GPL2+ only. See [ckeditor/ckeditor5#991](https://github.com/ckeditor/ckeditor5/issues/991) for more information.
|
||||
|
||||
|
||||
## [1.0.0-beta.4](https://github.com/ckeditor/ckeditor5-alignment/compare/v1.0.0-beta.2...v1.0.0-beta.4) (April 19, 2018)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Updated translations. ([586ae62](https://github.com/ckeditor/ckeditor5-alignment/commit/586ae62))
|
||||
|
||||
|
||||
## [1.0.0-beta.2](https://github.com/ckeditor/ckeditor5-alignment/compare/v1.0.0-beta.1...v1.0.0-beta.2) (April 10, 2018)
|
||||
|
||||
Internal changes only (updated dependencies, documentation, etc.).
|
||||
|
||||
|
||||
## [1.0.0-beta.1](https://github.com/ckeditor/ckeditor5-alignment/compare/v0.0.1...v1.0.0-beta.1) (March 15, 2018)
|
||||
|
||||
### Features
|
||||
|
||||
* Initial implementation. Closes [#2](https://github.com/ckeditor/ckeditor5-alignment/issues/2).
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
Software License Agreement
|
||||
==========================
|
||||
|
||||
**CKEditor 5 text alignment feature** – https://github.com/ckeditor/ckeditor5-alignment <br>
|
||||
Copyright (c) 2003–2024, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
|
||||
|
||||
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
||||
|
||||
Sources of Intellectual Property Included in CKEditor
|
||||
-----------------------------------------------------
|
||||
|
||||
Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
|
||||
|
||||
Trademarks
|
||||
----------
|
||||
|
||||
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com) All other brand and product names are trademarks, registered trademarks, or service marks of their respective holders.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
CKEditor 5 text alignment feature
|
||||
========================================
|
||||
|
||||
[](https://www.npmjs.com/package/@ckeditor/ckeditor5-alignment)
|
||||
[](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
|
||||
[](https://app.travis-ci.com/github/ckeditor/ckeditor5)
|
||||
|
||||
This package implements text alignment support for CKEditor 5.
|
||||
|
||||
## Demo
|
||||
|
||||
Check out the [demo in the text alignment feature guide](https://ckeditor.com/docs/ckeditor5/latest/features/text-alignment.html#demo).
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [`@ckeditor/ckeditor5-alignment` package](https://ckeditor.com/docs/ckeditor5/latest/api/alignment.html) page in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
|
||||
File diff suppressed because one or more lines are too long
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/af.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/af.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const e=n.af=n.af||{};e.dictionary=Object.assign(e.dictionary||{},{"Align center":"Belyn in die middel","Align left":"Belyn links","Align right":"Belyn regs",Justify:"Belyn beide kante","Text alignment":"Teksbelyning","Text alignment toolbar":"Teksbelyning nutsbank"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ar.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ar.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ar=n.ar||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"محاذاة في المنتصف","Align left":"محاذاة لليسار","Align right":"محاذاة لليمين",Justify:"ضبط","Text alignment":"محاذاة النص","Text alignment toolbar":"شريط أدوات محاذاة النص"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/az.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/az.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.az=n.az||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Mərkəzə düzləndir","Align left":"Soldan düzləndir","Align right":"Sağdan düzləndir",Justify:"Eninə görə","Text alignment":"Mətn düzləndirməsi","Text alignment toolbar":"Mətnin düzləndirmə paneli"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/bg.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/bg.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.bg=n.bg||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Централно подравняване","Align left":"Ляво подравняване","Align right":"Дясно подравняване",Justify:"Разпредели по равно","Text alignment":"Подравняване на текста","Text alignment toolbar":"Лента за подравняване на текст"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/bn.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/bn.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.bn=n.bn||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"কেন্দ্র সারিবদ্ধ করুন","Align left":"বামে সারিবদ্ধ করুন","Align right":"ডানদিকে সারিবদ্ধ করুন",Justify:"জাস্টিফাই","Text alignment":"টেক্সট সারিবদ্ধকরণ","Text alignment toolbar":"টেক্সট শ্রেণীবিন্যাস টুলবার"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/bs.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/bs.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const a=n.bs=n.bs||{};a.dictionary=Object.assign(a.dictionary||{},{"Align center":"Centrirati","Align left":"Lijevo poravnanje","Align right":"Desno poravnanje",Justify:"","Text alignment":"Poravnanje teksta","Text alignment toolbar":"Traka za poravnanje teksta"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ca.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ca.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const e=i.ca=i.ca||{};e.dictionary=Object.assign(e.dictionary||{},{"Align center":"Alineació centre","Align left":"Alineació esquerra","Align right":"Alineació dreta",Justify:"Justificar","Text alignment":"Alineació text","Text alignment toolbar":"Barra d'eines d'alineació de text"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/cs.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/cs.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const t=n.cs=n.cs||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Zarovnat na střed","Align left":"Zarovnat vlevo","Align right":"Zarovnat vpravo",Justify:"Zarovnat do bloku","Text alignment":"Zarovnání textu","Text alignment toolbar":"Panel nástrojů zarovnání textu"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/da.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/da.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const n=t.da=t.da||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Justér center","Align left":"Justér venstre","Align right":"Justér højre",Justify:"Justér","Text alignment":"Tekstjustering","Text alignment toolbar":"Tekstjustering værktøjslinje"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(t){const i=t["de-ch"]=t["de-ch"]||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Zentriert","Align left":"Linksbündig","Align right":"Rechtsbündig",Justify:"Blocksatz","Text alignment":"Textausrichtung","Text alignment toolbar":"Textausrichtung Werkzeugleiste"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/de.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/de.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const t=n.de=n.de||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Zentriert","Align left":"Linksbündig","Align right":"Rechtsbündig",Justify:"Blocksatz","Text alignment":"Textausrichtung","Text alignment toolbar":"Text-Ausrichtung Toolbar"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/el.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/el.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.el=n.el||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Στοίχιση στο κέντρο","Align left":"Στοίχιση αριστερά","Align right":"Στοίχιση δεξιά",Justify:"Πλήρης στοίχηση","Text alignment":"Στοίχιση κειμένου","Text alignment toolbar":"Γραμμή εργαλείων στοίχισης κειμένου"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(n){const t=n["en-au"]=n["en-au"]||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Align centre","Align left":"Align left","Align right":"Align right",Justify:"Justify","Text alignment":"Text alignment","Text alignment toolbar":"Text alignment toolbar"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n["en-gb"]=n["en-gb"]||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Align center","Align left":"Align left","Align right":"Align right",Justify:"Justify","Text alignment":"Text alignment","Text alignment toolbar":""})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(i){const e=i["es-co"]=i["es-co"]||{};e.dictionary=Object.assign(e.dictionary||{},{"Align center":"Centrar","Align left":"Alinear a la izquierda","Align right":"Alinear a la derecha",Justify:"Justificar","Text alignment":"Alineación de texto","Text alignment toolbar":"Herramientas de alineación de texto"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/es.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/es.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(e){const i=e.es=e.es||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Centrar","Align left":"Alinear a la izquierda","Align right":"Alinear a la derecha",Justify:"Justificar","Text alignment":"Alineación del texto","Text alignment toolbar":"Barra de herramientas de alineación del texto"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/et.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/et.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.et=n.et||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Keskjoondus","Align left":"Vasakjoondus","Align right":"Paremjoondus",Justify:"Rööpjoondus","Text alignment":"Teksti joondamine","Text alignment toolbar":"Teksti joonduse tööriistariba"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/fa.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/fa.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.fa=n.fa||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"تراز وسط","Align left":"تراز چپ","Align right":"تراز راست",Justify:"هم تراز کردن","Text alignment":"تراز متن","Text alignment toolbar":"نوار ابزار ترازبندی متن"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/fi.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/fi.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(a){const i=a.fi=a.fi||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Tasaa keskelle","Align left":"Tasaa vasemmalle","Align right":"Tasaa oikealle",Justify:"Tasaa molemmat reunat","Text alignment":"Tekstin tasaus","Text alignment toolbar":"Tekstin suuntauksen työkalupalkki"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/fr.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/fr.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(e){const t=e.fr=e.fr||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Centrer","Align left":"Aligner à gauche","Align right":"Aligner à droite",Justify:"Justifier","Text alignment":"Alignement du texte","Text alignment toolbar":"Barre d'outils d'alignement du texte"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/gl.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/gl.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const e=t.gl=t.gl||{};e.dictionary=Object.assign(e.dictionary||{},{"Align center":"Centrar horizontalmente","Align left":"Aliñar á esquerda","Align right":"Aliñar á dereita",Justify:"Xustificado","Text alignment":"Aliñamento do texto","Text alignment toolbar":"Barra de ferramentas de aliñamento de textos"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/he.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/he.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.he=n.he||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"יישור באמצע","Align left":"יישור לשמאל","Align right":"יישור לימין",Justify:"מרכוז גבולות","Text alignment":"יישור טקסט","Text alignment toolbar":"סרגל כלים יישור טקסט"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/hi.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/hi.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const n=i.hi=i.hi||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Align center","Align left":"Align left","Align right":"Align right",Justify:"Justify","Text alignment":"Text alignment","Text alignment toolbar":"Text alignment toolbar"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/hr.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/hr.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const a=n.hr=n.hr||{};a.dictionary=Object.assign(a.dictionary||{},{"Align center":"Poravnaj po sredini","Align left":"Poravnaj ulijevo","Align right":"Poravnaj udesno",Justify:"Razvuci","Text alignment":"Poravnanje teksta","Text alignment toolbar":"Traka za poravnanje"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/hu.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/hu.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const t=i.hu=i.hu||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Középre igazítás","Align left":"Balra igazítás","Align right":"Jobbra igazítás",Justify:"Sorkizárt","Text alignment":"Szöveg igazítása","Text alignment toolbar":"Szöveg igazítás eszköztár"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/id.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/id.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(a){const t=a.id=a.id||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Rata tengah","Align left":"Rata kiri","Align right":"Rata kanan",Justify:"Rata kanan-kiri","Text alignment":"Perataan teks","Text alignment toolbar":"Alat perataan teks"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/it.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/it.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const n=i.it=i.it||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Allinea al centro","Align left":"Allinea a sinistra","Align right":"Allinea a destra",Justify:"Giustifica","Text alignment":"Allineamento del testo","Text alignment toolbar":"Barra degli strumenti dell'allineamento"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ja.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ja.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ja=n.ja||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"中央揃え","Align left":"左揃え","Align right":"右揃え",Justify:"両端揃え","Text alignment":"文字揃え","Text alignment toolbar":"テキストの整列"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/jv.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/jv.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const t=n.jv=n.jv||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Rata tengah","Align left":"Rata kiwa","Align right":"Rata tengen",Justify:"Rata kiwa tengen","Text alignment":"Perataan seratan","Text alignment toolbar":""})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/kk.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/kk.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.kk=n.kk||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Ортадан туралау","Align left":"Солға туралау","Align right":"Оңға туралау",Justify:"","Text alignment":"Мәтінді туралау","Text alignment toolbar":"Мәтінді туралау құралдар тақтасы"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/km.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/km.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.km=n.km||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"តម្រឹមកណ្ដាល","Align left":"តម្រឹមឆ្វេង","Align right":"តម្រឹមស្ដាំ",Justify:"តម្រឹមសងខាង","Text alignment":"ការតម្រឹមអក្សរ","Text alignment toolbar":"របារឧបករណ៍តម្រឹមអក្សរ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ko.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ko.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ko=n.ko||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"가운데 정렬","Align left":"왼쪽 정렬","Align right":"오른쪽 정렬",Justify:"양쪽 정렬","Text alignment":"텍스트 정렬","Text alignment toolbar":"텍스트 정렬 툴바"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ku.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ku.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ku=n.ku||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"بەهێڵکردنی ناورەڕاست","Align left":"بەهێڵکردنی چەپ","Align right":"بەهێڵکردنی ڕاست",Justify:"هاوستوونی","Text alignment":"ڕیززکردنی تێکست","Text alignment toolbar":"تووڵامرازی ڕیززکردنی تێکست"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/lt.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/lt.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const t=i.lt=i.lt||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"Centruoti","Align left":"Lygiuoti į kairę","Align right":"Lygiuoti į dešinę",Justify:"Lygiuoti per visą plotį","Text alignment":"Teksto lygiavimas","Text alignment toolbar":"Teksto lygiavimo įrankių juosta"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/lv.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/lv.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const n=i.lv=i.lv||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Centrēt","Align left":"Pa kreisi","Align right":"Pa labi",Justify:"Izlīdzināt abas malas","Text alignment":"Teksta izlīdzināšana","Text alignment toolbar":"Teksta līdzināšanas rīkjosla"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ms.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ms.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(a){const n=a.ms=a.ms||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Jajarkan tengah","Align left":"Jajarkan kiri","Align right":"Jajarkan kiri",Justify:"Imbang","Text alignment":"Jajaran teks","Text alignment toolbar":"Bar alat capaian jajaran teks"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/nb.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/nb.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const n=t.nb=t.nb||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Midstill","Align left":"Venstrejuster","Align right":"Høyrejuster",Justify:"Blokkjuster","Text alignment":"Tekstjustering","Text alignment toolbar":""})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ne.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ne.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ne=n.ne||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"केन्द्र पङ्क्तिबद्ध गर्नुहोस्","Align left":"बायाँ पङ्क्तिबद्ध गर्नुहोस्","Align right":"दायाँ पङ्क्तिबद्ध गर्नुहोस्",Justify:"जस्टिफाइ गर्नुहोस्","Text alignment":"पाठ संरेखण","Text alignment toolbar":""})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/nl.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/nl.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const n=i.nl=i.nl||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Midden uitlijnen","Align left":"Links uitlijnen","Align right":"Rechts uitlijnen",Justify:"Volledig uitlijnen","Text alignment":"Tekst uitlijning","Text alignment toolbar":"Tekst uitlijning werkbalk"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/no.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/no.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const n=t.no=t.no||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Midtstill","Align left":"Venstrejuster","Align right":"Høyrejuster",Justify:"Blokkjuster","Text alignment":"Tekstjustering","Text alignment toolbar":"Verktøylinje for tekstjustering"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/pl.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/pl.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.pl=n.pl||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Wyrównaj do środka","Align left":"Wyrównaj do lewej","Align right":"Wyrównaj do prawej",Justify:"Wyrównaj obustronnie","Text alignment":"Wyrównanie tekstu","Text alignment toolbar":"Pasek narzędzi wyrównania tekstu"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(t){const i=t["pt-br"]=t["pt-br"]||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Centralizar","Align left":"Alinhar à esquerda","Align right":"Alinhar à direita",Justify:"Justificar","Text alignment":"Alinhamento do texto","Text alignment toolbar":"Ferramentas de alinhamento de texto"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/pt.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/pt.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const i=t.pt=t.pt||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Alinhar ao centro","Align left":"Alinhar à esquerda","Align right":"Alinhar à direita",Justify:"Justificar","Text alignment":"Alinhamento de texto","Text alignment toolbar":"Barra de alinhamento de texto"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ro.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ro.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const n=i.ro=i.ro||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Aliniază la centru","Align left":"Aliniază la stânga","Align right":"Aliniază la dreapta",Justify:"Aliniază stânga-dreapta","Text alignment":"Aliniere text","Text alignment toolbar":"Bara aliniere text"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ru.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ru.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ru=n.ru||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Выравнивание по центру","Align left":"Выравнивание по левому краю","Align right":"Выравнивание по правому краю",Justify:"Выравнивание по ширине","Text alignment":"Выравнивание текста","Text alignment toolbar":"Выравнивание"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sk.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sk.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const a=n.sk=n.sk||{};a.dictionary=Object.assign(a.dictionary||{},{"Align center":"Zarovnať na stred","Align left":"Zarovnať vľavo","Align right":"Zarovnať vpravo",Justify:"Do bloku","Text alignment":"Zarovnanie textu","Text alignment toolbar":"Panel nástrojov zarovnania textu"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sl.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sl.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(a){const n=a.sl=a.sl||{};n.dictionary=Object.assign(n.dictionary||{},{"Align center":"Sredinska poravnava","Align left":"Poravnava levo","Align right":"Poravnava desno",Justify:"Postavi na sredino","Text alignment":"Poravnava besedila","Text alignment toolbar":"Orodna vrstica besedila"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sq.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sq.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const i=t.sq=t.sq||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Radhit në mes","Align left":"Radhit majtas","Align right":"Radhit djathtas",Justify:"Plotësim","Text alignment":"Radhitja e tekstit","Text alignment toolbar":"Shiriti i rradhitjes së tekstit"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(n){const a=n["sr-latn"]=n["sr-latn"]||{};a.dictionary=Object.assign(a.dictionary||{},{"Align center":"Centralno ravnanje","Align left":"Levo ravnanje","Align right":"Desno ravnanje",Justify:"Obostrano ravnanje","Text alignment":"Ravnanje teksta","Text alignment toolbar":"Alatke za ravnanje teksta"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sr.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sr.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.sr=n.sr||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Централно равнанје","Align left":"Лево равнање","Align right":"Десно равнање",Justify:"Обострано равнање","Text alignment":"Равнање текста","Text alignment toolbar":"Алатке за равнање текста"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sv.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/sv.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(t){const e=t.sv=t.sv||{};e.dictionary=Object.assign(e.dictionary||{},{"Align center":"Centrera","Align left":"Vänsterjustera","Align right":"Högerjustera",Justify:"Justera till marginaler","Text alignment":"Textjustering","Text alignment toolbar":"Verktygsfält för textjustering"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/th.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/th.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const t=n.th=n.th||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"จัดกึ่งกลาง","Align left":"จัดชิดซ้าย","Align right":"จัดชิดขวา",Justify:"จัด(ขอบ)","Text alignment":"จัดตำแหน่งข้อความ","Text alignment toolbar":"แถบเครื่องมือจัดตำแหน่งข้อความ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/tk.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/tk.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(e){const i=e.tk=e.tk||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Merkeze deňleşdir","Align left":"Çepe deňleşdiriň","Align right":"Saga deňleşdiriň",Justify:"Akla","Text alignment":"Tekstiň deňleşdirilmegi","Text alignment toolbar":"Teksti deňleşdirmek gurallar paneli"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/tr.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/tr.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(a){const i=a.tr=a.tr||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Ortala","Align left":"Sola hizala","Align right":"Sağa hizala",Justify:"İki yana yasla","Text alignment":"Yazı hizalama","Text alignment toolbar":"Yazı Hizlama Araç Çubuğu"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ug.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ug.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ug=n.ug||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"ئوتتۇرىغا توغرىلاش","Align left":"سولغا توغرىلاش","Align right":"ئوڭغا توغرىلاش",Justify:"ئوڭ سولدىن توغرىلا","Text alignment":"تېكىست توغرىلاش","Text alignment toolbar":"تېكىست توغرىلاش قورالبالدىقى"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/uk.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/uk.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.uk=n.uk||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"По центру","Align left":"По лівому краю","Align right":"По правому краю",Justify:"По ширині","Text alignment":"Вирівнювання тексту","Text alignment toolbar":"Панель інструментів вирівнювання тексту"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ur.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/ur.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.ur=n.ur||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"درمیانی سیدھ","Align left":"بائیں سیدھ","Align right":"دائیں سیدھ",Justify:"برابر سیدھ","Text alignment":"متن کی سیدھ","Text alignment toolbar":"خانہ آلات برائے سیدھ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/uz.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/uz.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(i){const t=i.uz=i.uz||{};t.dictionary=Object.assign(t.dictionary||{},{"Align center":"O'rtada tekislash","Align left":"Chap tomonda tekislash","Align right":"O'ng tomonda tekislash",Justify:"Kengligi bo'yicha tekislash","Text alignment":"Matnni tekislash","Text alignment toolbar":"Tekislash"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/vi.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/vi.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.vi=n.vi||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"Canh giữa","Align left":"Canh trái","Align right":"Canh phải",Justify:"Canh đều","Text alignment":"Căn chỉnh văn bản","Text alignment toolbar":"Thanh công cụ canh chữ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n["zh-cn"]=n["zh-cn"]||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"居中对齐","Align left":"左对齐","Align right":"右对齐",Justify:"两端对齐","Text alignment":"对齐","Text alignment toolbar":"对齐工具栏"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/zh.js
generated
vendored
1
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/build/translations/zh.js
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(n){const i=n.zh=n.zh||{};i.dictionary=Object.assign(i.dictionary||{},{"Align center":"置中對齊","Align left":"靠左對齊","Align right":"靠右對齊",Justify:"左右對齊","Text alignment":"文字對齊","Text alignment toolbar":"文字對齊"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
||||
31
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/ckeditor5-metadata.json
generated
vendored
31
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/ckeditor5-metadata.json
generated
vendored
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"plugins": [
|
||||
{
|
||||
"name": "Alignment",
|
||||
"className": "Alignment",
|
||||
"path": "src/alignment.js",
|
||||
"description": "Enables support for text alignment. You can use it to align your content to left, right and center or to justify it.",
|
||||
"docs": "features/text-alignment.html",
|
||||
"uiComponents": [
|
||||
{
|
||||
"type": "SplitButton",
|
||||
"name": "alignment",
|
||||
"iconPath": "@ckeditor/ckeditor5-core/theme/icons/align-left.svg"
|
||||
}
|
||||
],
|
||||
"htmlOutput": [
|
||||
{
|
||||
"elements": "$block",
|
||||
"styles": "text-align",
|
||||
"_comment": "By default, the alignment feature uses the `text-align` inline style."
|
||||
},
|
||||
{
|
||||
"elements": "$block",
|
||||
"classes": "*",
|
||||
"isAlternative": true,
|
||||
"_comment": "If `config.alignment.options` is set, these classes are used for alignment instead of inline styles."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"Align left": "Toolbar button tooltip for aligning the text to the left.",
|
||||
"Align right": "Toolbar button tooltip for aligning the text to the right.",
|
||||
"Align center": "Toolbar button tooltip for aligning the text to center.",
|
||||
"Justify": "Toolbar button tooltip for making the text justified.",
|
||||
"Text alignment": "Dropdown button tooltip for the text alignment feature.",
|
||||
"Text alignment toolbar": "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
}
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/af.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/af.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Afrikaans (https://app.transifex.com/ckeditor/teams/11143/af/)\n"
|
||||
"Language: af\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Belyn links"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Belyn regs"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Belyn in die middel"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Belyn beide kante"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Teksbelyning"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Teksbelyning nutsbank"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/ar.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/ar.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Arabic (https://app.transifex.com/ckeditor/teams/11143/ar/)\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "محاذاة لليسار"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "محاذاة لليمين"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "محاذاة في المنتصف"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "ضبط"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "محاذاة النص"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "شريط أدوات محاذاة النص"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/az.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/az.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Azerbaijani (https://app.transifex.com/ckeditor/teams/11143/az/)\n"
|
||||
"Language: az\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Soldan düzləndir"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Sağdan düzləndir"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Mərkəzə düzləndir"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Eninə görə"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Mətn düzləndirməsi"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Mətnin düzləndirmə paneli"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/bg.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/bg.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Bulgarian (https://app.transifex.com/ckeditor/teams/11143/bg/)\n"
|
||||
"Language: bg\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Ляво подравняване"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Дясно подравняване"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Централно подравняване"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Разпредели по равно"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Подравняване на текста"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Лента за подравняване на текст"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/bn.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/bn.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Bengali (https://app.transifex.com/ckeditor/teams/11143/bn/)\n"
|
||||
"Language: bn\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "বামে সারিবদ্ধ করুন"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "ডানদিকে সারিবদ্ধ করুন"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "কেন্দ্র সারিবদ্ধ করুন"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "জাস্টিফাই"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "টেক্সট সারিবদ্ধকরণ"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "টেক্সট শ্রেণীবিন্যাস টুলবার"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/bs.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/bs.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Bosnian (https://app.transifex.com/ckeditor/teams/11143/bs/)\n"
|
||||
"Language: bs\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Lijevo poravnanje"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Desno poravnanje"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Centrirati"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Poravnanje teksta"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Traka za poravnanje teksta"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/ca.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/ca.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Catalan (https://app.transifex.com/ckeditor/teams/11143/ca/)\n"
|
||||
"Language: ca\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Alineació esquerra"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Alineació dreta"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Alineació centre"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Justificar"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Alineació text"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Barra d'eines d'alineació de text"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/cs.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/cs.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Czech (https://app.transifex.com/ckeditor/teams/11143/cs/)\n"
|
||||
"Language: cs\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Zarovnat vlevo"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Zarovnat vpravo"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Zarovnat na střed"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Zarovnat do bloku"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Zarovnání textu"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Panel nástrojů zarovnání textu"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/da.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/da.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Danish (https://app.transifex.com/ckeditor/teams/11143/da/)\n"
|
||||
"Language: da\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Justér venstre"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Justér højre"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Justér center"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Justér"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Tekstjustering"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Tekstjustering værktøjslinje"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/de-ch.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/de-ch.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: German (Switzerland) (https://app.transifex.com/ckeditor/teams/11143/de_CH/)\n"
|
||||
"Language: de_CH\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Linksbündig"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Rechtsbündig"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Zentriert"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Blocksatz"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Textausrichtung"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Textausrichtung Werkzeugleiste"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/de.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/de.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: German (https://app.transifex.com/ckeditor/teams/11143/de/)\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Linksbündig"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Rechtsbündig"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Zentriert"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Blocksatz"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Textausrichtung"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Text-Ausrichtung Toolbar"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/el.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/el.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: Greek (https://app.transifex.com/ckeditor/teams/11143/el/)\n"
|
||||
"Language: el\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Στοίχιση αριστερά"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Στοίχιση δεξιά"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Στοίχιση στο κέντρο"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Πλήρης στοίχηση"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Στοίχιση κειμένου"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Γραμμή εργαλείων στοίχισης κειμένου"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/en-au.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/en-au.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: English (Australia) (https://app.transifex.com/ckeditor/teams/11143/en_AU/)\n"
|
||||
"Language: en_AU\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Align left"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Align right"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Align centre"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Justify"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Text alignment"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr "Text alignment toolbar"
|
||||
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/en-gb.po
generated
vendored
42
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-alignment/lang/translations/en-gb.po
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
#
|
||||
# Before you edit this file, please keep in mind that contributing to the project
|
||||
# translations is possible ONLY via the Transifex online service.
|
||||
#
|
||||
# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
|
||||
#
|
||||
# To learn more, check out the official contributor's guide:
|
||||
# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language-Team: English (United Kingdom) (https://app.transifex.com/ckeditor/teams/11143/en_GB/)\n"
|
||||
"Language: en_GB\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the left."
|
||||
msgid "Align left"
|
||||
msgstr "Align left"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to the right."
|
||||
msgid "Align right"
|
||||
msgstr "Align right"
|
||||
|
||||
msgctxt "Toolbar button tooltip for aligning the text to center."
|
||||
msgid "Align center"
|
||||
msgstr "Align center"
|
||||
|
||||
msgctxt "Toolbar button tooltip for making the text justified."
|
||||
msgid "Justify"
|
||||
msgstr "Justify"
|
||||
|
||||
msgctxt "Dropdown button tooltip for the text alignment feature."
|
||||
msgid "Text alignment"
|
||||
msgstr "Text alignment"
|
||||
|
||||
msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
|
||||
msgid "Text alignment toolbar"
|
||||
msgstr ""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue