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,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
});
|
});
|
||||||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const [dateFilter, setDateFilter] = React.useState("");
|
const [dateFilter, setDateFilter] = React.useState("");
|
||||||
const [endDate, setEndDate] = React.useState("");
|
const [endDate, setEndDate] = React.useState("");
|
||||||
const [filterByCode, setFilterByCode] = React.useState<string>("");
|
const [filterByCode, setFilterByCode] = React.useState<string>("");
|
||||||
|
|
@ -113,7 +113,15 @@ const TaskTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit, isSpecificAttention, search, dateFilter, filterByCode]);
|
}, [
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
isSpecificAttention,
|
||||||
|
search,
|
||||||
|
dateFilter,
|
||||||
|
filterByCode,
|
||||||
|
statusFilter,
|
||||||
|
]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const formattedStartDate = dateFilter
|
const formattedStartDate = dateFilter
|
||||||
|
|
@ -126,10 +134,22 @@ const TaskTable = () => {
|
||||||
limit,
|
limit,
|
||||||
filterByCode,
|
filterByCode,
|
||||||
formattedStartDate,
|
formattedStartDate,
|
||||||
isSpecificAttention ? "atensi-khusus" : "tugas-harian"
|
isSpecificAttention ? "atensi-khusus" : "tugas-harian",
|
||||||
|
statusFilter
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
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) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * limit + index + 1;
|
||||||
});
|
});
|
||||||
|
|
@ -145,24 +165,25 @@ const TaskTable = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value); // Perbarui state search
|
setFilterByCode(e.target.value);
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
setSearch(e.target.value);
|
||||||
|
table.getColumn("judul")?.setFilterValue(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleStatusCheckboxChange(value: any) {
|
function handleStatusCheckboxChange(value: number) {
|
||||||
setStatusFilter((prev: any) =>
|
setStatusFilter((prev) =>
|
||||||
prev.includes(value)
|
prev.includes(value)
|
||||||
? prev.filter((status: any) => status !== value)
|
? prev.filter((status) => status !== value)
|
||||||
: [...prev, value]
|
: [...prev, value]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearchFilterByCode = (e: React.ChangeEvent<HTMLInputElement>) => {
|
// const handleSearchFilterByCode = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = e.target.value;
|
// const value = e.target.value;
|
||||||
console.log("code :", value);
|
// console.log("code :", value);
|
||||||
setFilterByCode(value);
|
// setFilterByCode(value);
|
||||||
fetchData();
|
// fetchData();
|
||||||
};
|
// };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
|
|
@ -211,7 +232,7 @@ const TaskTable = () => {
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
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"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white w-full"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
|
|
@ -243,25 +264,25 @@ const TaskTable = () => {
|
||||||
className="max-w-sm"
|
className="max-w-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-2 my-1">
|
{/* <div className="mx-2 my-1">
|
||||||
<Label>Code</Label>
|
<Label>Code</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter Status..."
|
placeholder="Filter Status..."
|
||||||
value={filterByCode}
|
value={filterByCode}
|
||||||
onChange={handleSearchFilterByCode}
|
// onChange={handleSearchFilterByCode}
|
||||||
className="max-w-sm"
|
className="max-w-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
<Label className="ml-2 mt-2">Status</Label>
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
<div className="flex items-center px-4 py-1">
|
<div className="flex items-center px-4 py-1">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="status-2"
|
id="status-1"
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
checked={statusFilter.includes(1)}
|
checked={statusFilter.includes(1)}
|
||||||
onChange={() => handleStatusCheckboxChange(1)}
|
onChange={() => handleStatusCheckboxChange(1)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="status-2" className="text-sm">
|
<label htmlFor="status-1" className="text-sm">
|
||||||
Selesai
|
Selesai
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -281,7 +302,7 @@ const TaskTable = () => {
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
{/* <div className="flex-none">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter Status..."
|
placeholder="Filter Status..."
|
||||||
value={
|
value={
|
||||||
|
|
@ -292,7 +313,7 @@ const TaskTable = () => {
|
||||||
}
|
}
|
||||||
className="max-w-sm "
|
className="max-w-sm "
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<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 { Item } from "@radix-ui/react-dropdown-menu";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -921,7 +922,7 @@ export default function FormAudio() {
|
||||||
}`}
|
}`}
|
||||||
onClick={() => handleArticleIdClick(id)}
|
onClick={() => handleArticleIdClick(id)}
|
||||||
>
|
>
|
||||||
{id}
|
{"Narasi " + (index + 1)}
|
||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -930,18 +931,8 @@ export default function FormAudio() {
|
||||||
<div className="pt-3">
|
<div className="pt-3">
|
||||||
<div className="flex flex-row justify-between items-center">
|
<div className="flex flex-row justify-between items-center">
|
||||||
{selectedArticleId && (
|
{selectedArticleId && (
|
||||||
<a
|
<Link
|
||||||
href={`/admin/media/${
|
href={`/contributor/content/audio/update-seo/${selectedArticleId}`}
|
||||||
fileTypeId === "1"
|
|
||||||
? "image"
|
|
||||||
: fileTypeId === "2"
|
|
||||||
? "video"
|
|
||||||
: fileTypeId === "3"
|
|
||||||
? "text"
|
|
||||||
: "audio"
|
|
||||||
}/update-new/${selectedArticleId}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
|
|
@ -951,7 +942,7 @@ export default function FormAudio() {
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 { options } from "@fullcalendar/core/preact.js";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -939,18 +940,8 @@ export default function FormImage() {
|
||||||
<div className="pt-3">
|
<div className="pt-3">
|
||||||
<div className="flex flex-row justify-between items-center">
|
<div className="flex flex-row justify-between items-center">
|
||||||
{selectedArticleId && (
|
{selectedArticleId && (
|
||||||
<a
|
<Link
|
||||||
href={`/admin/media/${
|
href={`/contributor/content/image/update-seo/${selectedArticleId}`}
|
||||||
fileTypeId === "1"
|
|
||||||
? "image"
|
|
||||||
: fileTypeId === "2"
|
|
||||||
? "video"
|
|
||||||
: fileTypeId === "3"
|
|
||||||
? "text"
|
|
||||||
: "audio"
|
|
||||||
}/update-new/${selectedArticleId}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
|
|
@ -960,7 +951,7 @@ export default function FormImage() {
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 {
|
import {
|
||||||
convertSPIT,
|
convertSPIT,
|
||||||
createMedia,
|
createMedia,
|
||||||
|
deleteSPIT,
|
||||||
detailSPIT,
|
detailSPIT,
|
||||||
getTagsBySubCategoryId,
|
getTagsBySubCategoryId,
|
||||||
listCategory,
|
listCategory,
|
||||||
|
|
@ -47,6 +48,7 @@ import { request } from "http";
|
||||||
import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
|
import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { error } from "@/lib/swal";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
|
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 (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -922,8 +967,12 @@ export default function FormConvertSPIT() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" color="primary" variant="outline">
|
<Button
|
||||||
Cancel
|
type="submit"
|
||||||
|
className="bg-red-500 hover:bg-red-700"
|
||||||
|
onClick={() => deleteSpitContent()}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
|
||||||
import { Item } from "@radix-ui/react-dropdown-menu";
|
import { Item } from "@radix-ui/react-dropdown-menu";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -919,7 +920,7 @@ export default function FormTeks() {
|
||||||
}`}
|
}`}
|
||||||
onClick={() => handleArticleIdClick(id)}
|
onClick={() => handleArticleIdClick(id)}
|
||||||
>
|
>
|
||||||
{id}
|
{"Narasi " + (index + 1)}
|
||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -928,18 +929,8 @@ export default function FormTeks() {
|
||||||
<div className="pt-3">
|
<div className="pt-3">
|
||||||
<div className="flex flex-row justify-between items-center">
|
<div className="flex flex-row justify-between items-center">
|
||||||
{selectedArticleId && (
|
{selectedArticleId && (
|
||||||
<a
|
<Link
|
||||||
href={`/admin/media/${
|
href={`/contributor/content/teks/update-seo/${selectedArticleId}`}
|
||||||
fileTypeId === "1"
|
|
||||||
? "image"
|
|
||||||
: fileTypeId === "2"
|
|
||||||
? "video"
|
|
||||||
: fileTypeId === "3"
|
|
||||||
? "text"
|
|
||||||
: "audio"
|
|
||||||
}/update-new/${selectedArticleId}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
|
|
@ -949,7 +940,7 @@ export default function FormTeks() {
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 { Item } from "@radix-ui/react-dropdown-menu";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { getCsrfToken } from "@/service/auth";
|
import { getCsrfToken } from "@/service/auth";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -917,7 +918,7 @@ export default function FormVideo() {
|
||||||
}`}
|
}`}
|
||||||
onClick={() => handleArticleIdClick(id)}
|
onClick={() => handleArticleIdClick(id)}
|
||||||
>
|
>
|
||||||
{id}
|
{"Narasi " + (index + 1)}
|
||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -926,18 +927,8 @@ export default function FormVideo() {
|
||||||
<div className="pt-3">
|
<div className="pt-3">
|
||||||
<div className="flex flex-row justify-between items-center">
|
<div className="flex flex-row justify-between items-center">
|
||||||
{selectedArticleId && (
|
{selectedArticleId && (
|
||||||
<a
|
<Link
|
||||||
href={`/admin/media/${
|
href={`/contributor/content/video/update-seo/${selectedArticleId}`}
|
||||||
fileTypeId === "1"
|
|
||||||
? "image"
|
|
||||||
: fileTypeId === "2"
|
|
||||||
? "video"
|
|
||||||
: fileTypeId === "3"
|
|
||||||
? "text"
|
|
||||||
: "audio"
|
|
||||||
}/update-new/${selectedArticleId}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
|
|
@ -947,7 +938,7 @@ export default function FormVideo() {
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 userLevelNumber = getCookiesDecrypt("ulne");
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = "";
|
||||||
|
|
||||||
// State for various form fields
|
// State for various form fields
|
||||||
const [taskOutput, setTaskOutput] = useState({
|
const [taskOutput, setTaskOutput] = useState({
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -24,6 +24,7 @@
|
||||||
"@fullcalendar/timegrid": "^6.1.15",
|
"@fullcalendar/timegrid": "^6.1.15",
|
||||||
"@hookform/resolvers": "^3.9.0",
|
"@hookform/resolvers": "^3.9.0",
|
||||||
"@iconify/react": "^5.0.2",
|
"@iconify/react": "^5.0.2",
|
||||||
|
"@mui/x-charts": "^7.25.0",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.0.3",
|
"@radix-ui/react-aspect-ratio": "^1.0.3",
|
||||||
|
|
@ -68,8 +69,10 @@
|
||||||
"@types/sanitize-html": "^2.13.0",
|
"@types/sanitize-html": "^2.13.0",
|
||||||
"@vercel/analytics": "^1.3.1",
|
"@vercel/analytics": "^1.3.1",
|
||||||
"@wavesurfer/react": "^1.0.8",
|
"@wavesurfer/react": "^1.0.8",
|
||||||
|
"add": "^2.0.6",
|
||||||
"apexcharts": "^3.49.2",
|
"apexcharts": "^3.49.2",
|
||||||
"axios": "^1.7.8",
|
"axios": "^1.7.8",
|
||||||
|
"chart": "^0.1.2",
|
||||||
"chart.js": "^4.4.3",
|
"chart.js": "^4.4.3",
|
||||||
"ckeditor5-custom-build": "file:vendor/ckeditor5",
|
"ckeditor5-custom-build": "file:vendor/ckeditor5",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
|
@ -133,6 +136,7 @@
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
"rtl-detect": "^1.1.2",
|
"rtl-detect": "^1.1.2",
|
||||||
"sanitize-html": "^2.14.0",
|
"sanitize-html": "^2.14.0",
|
||||||
|
"shadcn": "^2.3.0",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.4",
|
||||||
"sonner": "^1.5.0",
|
"sonner": "^1.5.0",
|
||||||
"sweetalert2": "^11.10.5",
|
"sweetalert2": "^11.10.5",
|
||||||
|
|
|
||||||
|
|
@ -214,3 +214,8 @@ export async function deleteFile(data: any) {
|
||||||
const url = "media/file";
|
const url = "media/file";
|
||||||
return httpDeleteInterceptor(url, data);
|
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,
|
size: any,
|
||||||
code: any,
|
code: any,
|
||||||
createdAt: any,
|
createdAt: any,
|
||||||
taskType: string
|
taskType: string,
|
||||||
|
status: number[]
|
||||||
) {
|
) {
|
||||||
|
const statusQuery = status.length
|
||||||
|
? `&isDone=${status.includes(1)}&isActive=${status.includes(2)}`
|
||||||
|
: "";
|
||||||
|
|
||||||
return httpGetInterceptor(
|
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