feat:docx

This commit is contained in:
Rama Priyanto 2025-04-07 16:03:07 +07:00
parent 673bd461db
commit 51d8feb75c
5 changed files with 287 additions and 7 deletions

View File

@ -2685,3 +2685,30 @@ export const PlayIcon = ({
</g>
</svg>
);
export const ExportIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
{...props}
viewBox="0 0 36 36"
>
<path
fill="currentColor"
d="M17 22a1 1 0 0 1 1-1h8V6a2 2 0 0 0-2-2H10.87L4 10.86V30a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2v-7h-8a1 1 0 0 1-1-1m-5-10H6v-.32L11.69 6H12Z"
className="clr-i-solid clr-i-solid-path-1"
/>
<path
fill="currentColor"
d="M29.32 16.35a1 1 0 0 0-1.41 1.41L31.16 21H26v2h5.19l-3.28 3.28a1 1 0 1 0 1.41 1.41L35 22Z"
className="clr-i-solid clr-i-solid-path-2"
/>
<path fill="none" d="M0 0h36v36H0z" />
</svg>
);

View File

@ -239,7 +239,7 @@ export default function MedolUpdate() {
},
}}
pagination={true}
className="mySwiper"
className="mySwiper custom-button-slider !px-[8px]"
onSwiper={(swiper) => {
swiper.navigation.nextEl?.classList.add(
"bg-white/70",
@ -328,7 +328,7 @@ export default function MedolUpdate() {
},
}}
pagination={true}
className="mySwiper"
className="mySwiper custom-button-slider !px-[8px]"
onSwiper={(swiper) => {
swiper.navigation.nextEl?.classList.add(
"bg-white/70",
@ -401,11 +401,8 @@ export default function MedolUpdate() {
)}
</Tab>
<Tab key="polritv" title="Polri TV Update">
<div className="w-full">
<div className="w-[40%] mx-auto">
<PolriTvWidget />
</div>
</div>
<PolriTvWidget />
<div className="text-center pt-6">
<Link
href="https://www.youtube.com/@TvRadioPolri"

View File

@ -5,6 +5,7 @@ import {
CreateIconIon,
DeleteIcon,
DotsYIcon,
ExportIcon,
EyeIconMdi,
SearchIcon,
TimesIcon,
@ -67,6 +68,23 @@ import {
getFeedbacksById,
} from "@/service/feedbacks";
import * as XLSX from "xlsx";
import {
Document,
Packer,
Paragraph,
Table as DocxTable,
TableCell as DocxTableCell,
TableRow as DocxTableRow,
TextRun,
WidthType,
} from "docx";
interface ExportData {
no: string;
commentFromName: string;
commentFromEmail: string;
message: string;
}
const columns = [
{ name: "No", uid: "no" },
@ -257,6 +275,20 @@ export default function SuggestionsTable() {
<CreateIconIon className="inline mr-2 mb-1" />
Detail
</DropdownItem>
<DropdownItem
key="export"
onPress={() =>
exportToWord({
no: suggestion.no,
commentFromName: suggestion.commentFromName,
commentFromEmail: suggestion.commentFromEmail,
message: suggestion.message,
})
}
>
<ExportIcon className="inline mr-2 mb-1" />
Export
</DropdownItem>
<DropdownItem
key="delete"
@ -347,6 +379,71 @@ export default function SuggestionsTable() {
XLSX.writeFile(workbook, `Kritik Saran.xlsx`);
};
const exportToWord = async (data: ExportData) => {
const tableRows = [
new DocxTableRow({
children: ["No", "Nama", "Email", "Kritik & Saran"].map(
(text) =>
new DocxTableCell({
children: [
new Paragraph({
children: [new TextRun({ text, bold: true })],
}),
],
width: { size: 25, type: WidthType.PERCENTAGE },
})
),
}),
];
tableRows.push(
new DocxTableRow({
children: [
String(data.no) || "",
data.commentFromName || "",
data.commentFromEmail || "",
data.message || "",
].map(
(text) =>
new DocxTableCell({
children: [new Paragraph(text)],
width: { size: 25, type: WidthType.PERCENTAGE },
})
),
})
);
const doc = new Document({
sections: [
{
children: [
new Paragraph({
text: "Kritik & Saran",
heading: "Heading1",
}),
new DocxTable({
rows: tableRows,
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
}),
],
},
],
});
const blob = await Packer.toBlob(doc);
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "KritikSaran.docx";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return (
<>
<div className="flex flex-row gap-2 items-center">

158
package-lock.json generated
View File

@ -35,6 +35,7 @@
"ckeditor5-custom-build": "file:vendor/ckeditor5",
"clsx": "^2.0.0",
"dayjs": "^1.11.13",
"docx": "^9.3.0",
"dompurify": "^3.2.0",
"eslint": "8.48.0",
"eslint-config-next": "14.0.2",
@ -6859,6 +6860,11 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@ -7344,6 +7350,47 @@
"node": ">=6.0.0"
}
},
"node_modules/docx": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/docx/-/docx-9.3.0.tgz",
"integrity": "sha512-IQwbSLBEMKNJmP9CDqSu3vJDhJps6tv/DlxsxuvfTclapd0JPCz1IFJTU//WdTjUenPMFaUD2Kg1nkQb/BWPIg==",
"dependencies": {
"@types/node": "^22.7.5",
"hash.js": "^1.1.7",
"jszip": "^3.10.1",
"nanoid": "^5.1.3",
"xml": "^1.0.1",
"xml-js": "^1.6.8"
},
"engines": {
"node": ">=10"
}
},
"node_modules/docx/node_modules/@types/node": {
"version": "22.14.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz",
"integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/docx/node_modules/nanoid": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@ -8702,6 +8749,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"dependencies": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -8788,6 +8844,11 @@
"node": ">= 4"
}
},
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@ -9494,6 +9555,49 @@
"node": ">=4.0"
}
},
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/jszip/node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/jszip/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/jszip/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/jszip/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -9548,6 +9652,14 @@
"node": ">= 0.8.0"
}
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
@ -9740,6 +9852,11 @@
"node": ">=6"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -10242,6 +10359,11 @@
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -11184,6 +11306,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@ -11760,6 +11887,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sax": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
},
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@ -11857,6 +11989,11 @@
"node": ">= 0.4"
}
},
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"node_modules/shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@ -13076,6 +13213,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
},
"node_modules/unique-filename": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@ -13779,6 +13921,22 @@
"node": ">=0.8"
}
},
"node_modules/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="
},
"node_modules/xml-js": {
"version": "1.6.11",
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
"dependencies": {
"sax": "^1.2.4"
},
"bin": {
"xml-js": "bin/cli.js"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

View File

@ -36,6 +36,7 @@
"ckeditor5-custom-build": "file:vendor/ckeditor5",
"clsx": "^2.0.0",
"dayjs": "^1.11.13",
"docx": "^9.3.0",
"dompurify": "^3.2.0",
"eslint": "8.48.0",
"eslint-config-next": "14.0.2",