fix: fixed in register

This commit is contained in:
sabdayagra 2025-01-10 15:35:26 +07:00
commit 39627c8ee8
3011 changed files with 7789 additions and 343186 deletions

View File

@ -124,7 +124,6 @@ const AddExpertTable = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {

View File

@ -69,6 +69,7 @@ import { Link } from "@/i18n/routing";
const BroadcastTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [search, setSearch] = React.useState("");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
@ -119,7 +120,6 @@ const BroadcastTable = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
@ -147,7 +147,7 @@ const BroadcastTable = () => {
const res = await listDataMedia(
page - 1,
showData,
"",
search,
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",")
);
@ -218,6 +218,7 @@ const BroadcastTable = () => {
placeholder="Search"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onChange={(e) => setSearch(e.target.value)}
className="max-w-[300px]"
/>
<div className="flex flex-row gap-2">

View File

@ -0,0 +1,291 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
getUserById,
saveUserInternal,
} from "@/service/management-user/management-user";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Link, useRouter } from "@/i18n/routing";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { useParams } from "next/navigation";
import { identity } from "@fullcalendar/core/internal";
const FormSchema = z.object({
fullname: z.string({
required_error: "Required",
}),
username: z.string({
required_error: "Required",
}),
identity: z.string({
required_error: "Required",
}),
address: z.string({
required_error: "Required",
}),
email: z.string({
required_error: "Required",
}),
phoneNumber: z.string({
required_error: "Required",
}),
password: z.string({
required_error: "Required",
}),
confirmPassword: z.string({
required_error: "Required",
}),
isValidPassword: z.boolean().refine((val) => val === true, {
message: "Check Password",
}),
});
export default function EditUserForm() {
const router = useRouter();
const params = useParams();
const id = params?.id;
const MySwal = withReactContent(Swal);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
password: "",
confirmPassword: "",
},
});
const passwordVal = form.watch("password");
const confPasswordVal = form.watch("confirmPassword");
useEffect(() => {
initData();
}, []);
const initData = async () => {
loading();
const response = await getUserById(String(id));
const res = response?.data?.data;
close();
console.log("res", res);
form.setValue("fullname", res?.fullname);
form.setValue("username", res?.username);
form.setValue("phoneNumber", res?.phoneNumber);
form.setValue("address", res?.address);
form.setValue("email", res?.email);
};
async function save(data: z.infer<typeof FormSchema>) {
let req: any = {
id: Number(id),
firstName: data.fullname,
username: data.username,
address: data.address,
email: data.email,
identityNumber: data.identity,
password: data.password,
passwordConf: data.confirmPassword,
isDefault: false,
};
loading();
const response = await saveUserInternal(req);
if (response?.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "Oke",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");
}
});
return false;
}
async function onSubmit(data: z.infer<typeof FormSchema>) {
MySwal.fire({
title: "Simpan Data?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
return (
<div>
<SiteBreadcrumb />
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 bg-white p-10 w-full"
>
<p className="text-xl">Data Pengelola Media Hub</p>
<FormField
control={form.control}
name="fullname"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Lengkap</FormLabel>
<FormControl>
<Input
placeholder="Masukkan nama lengkap"
{...field}
className="w-1/2"
readOnly
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Masukkan username"
{...field}
className="w-1/2"
readOnly
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="identity"
render={({ field }) => (
<FormItem>
<FormLabel>Nomor Identitas</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor identitas"
{...field}
className="w-1/2"
readOnly
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Alamat</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan alamat"
className="resize-none w-1/2"
{...field}
readOnly
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="Masukkan email"
{...field}
className="w-1/2"
readOnly
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>No. Handphone</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor handphone"
{...field}
className="w-1/2 mb-5"
readOnly
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Link href="/admin/management-user">
<Button type="button" color="primary" variant="outline">
Back
</Button>
</Link>
</form>
</Form>
</div>
);
}

View File

@ -0,0 +1,381 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { zodResolver } from "@hookform/resolvers/zod";
import { Check, ChevronsUpDown } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
AdministrationLevelList,
getListCompetencies,
getListEducation,
getListSchools,
getUserById,
saveUserInternal,
} from "@/service/management-user/management-user";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Link, useRouter } from "@/i18n/routing";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import dynamic from "next/dynamic";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { useParams } from "next/navigation";
import { identity } from "@fullcalendar/core/internal";
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
ssr: false,
});
interface RoleData {
id: number;
label: string;
name: string;
value: string;
levelNumber: number;
}
const FormSchema = z.object({
fullname: z.string({
required_error: "Required",
}),
username: z.string({
required_error: "Required",
}),
identity: z.string({
required_error: "Required",
}),
address: z.string({
required_error: "Required",
}),
email: z.string({
required_error: "Required",
}),
phoneNumber: z.string({
required_error: "Required",
}),
password: z.string({
required_error: "Required",
}),
confirmPassword: z.string({
required_error: "Required",
}),
isValidPassword: z.boolean().refine((val) => val === true, {
message: "Check Password",
}),
});
export default function EditUserForm() {
const router = useRouter();
const params = useParams();
const id = params?.id;
const MySwal = withReactContent(Swal);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
password: "",
confirmPassword: "",
},
});
const passwordVal = form.watch("password");
const confPasswordVal = form.watch("confirmPassword");
useEffect(() => {
initData();
}, []);
const initData = async () => {
loading();
const response = await getUserById(String(id));
const res = response?.data?.data;
close();
console.log("res", res);
form.setValue("fullname", res?.fullname);
form.setValue("username", res?.username);
form.setValue("phoneNumber", res?.phoneNumber);
form.setValue("address", res?.address);
form.setValue("email", res?.email);
};
async function save(data: z.infer<typeof FormSchema>) {
let req: any = {
id: Number(id),
firstName: data.fullname,
username: data.username,
address: data.address,
email: data.email,
identityNumber: data.identity,
password: data.password,
passwordConf: data.confirmPassword,
isDefault: false,
};
loading();
const response = await saveUserInternal(req);
if (response?.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "Oke",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");
}
});
return false;
}
async function onSubmit(data: z.infer<typeof FormSchema>) {
MySwal.fire({
title: "Simpan Data?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
return (
<div>
<SiteBreadcrumb />
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 bg-white p-10 w-full"
>
<p className="text-xl">Data Pengelola Media Hub</p>
<FormField
control={form.control}
name="fullname"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Lengkap</FormLabel>
<FormControl>
<Input
placeholder="Masukkan nama lengkap"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Masukkan username"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="identity"
render={({ field }) => (
<FormItem>
<FormLabel>Nomor Identitas</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor identitas"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Alamat</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan alamat"
className="resize-none w-1/2"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="Masukkan email"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>No. Handphone</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor handphone"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Masukkan kata sandi"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="confirmPassword"
render={({ field }) => (
<FormItem>
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Masukkan kata sandi"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<PasswordChecklist
rules={["minLength", "specialChar", "number", "capital", "match"]}
minLength={8}
value={passwordVal}
valueAgain={confPasswordVal}
onChange={(isValid) => {
form.setValue("isValidPassword", isValid);
}}
className="text-sm"
messages={{
minLength: "Password harus lebih dari 8 karakter",
specialChar: "Password harus memiliki spesial karakter",
number: "Password harus memiliki angka",
capital: "Password harus memiliki huruf kapital",
match: "Password sama",
}}
/>
<Link href="/admin/management-user">
<Button type="button" color="primary" variant="outline">
Back
</Button>
</Link>
<Button type="submit" color="primary" className="mx-3">
Submit
</Button>
</form>
</Form>
</div>
);
}

View File

@ -0,0 +1,752 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { zodResolver } from "@hookform/resolvers/zod";
import { Check, ChevronsUpDown } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
AdministrationLevelList,
getListCompetencies,
getListEducation,
getListSchools,
saveUserInternal,
} from "@/service/management-user/management-user";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Link, useRouter } from "@/i18n/routing";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import dynamic from "next/dynamic";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
ssr: false,
});
const sns = [
{
key: 1,
id: "comment",
typeId: 1,
name: "Komentar Konten",
},
{
key: 2,
id: "fb",
typeId: 2,
name: "Facebook",
},
{
key: 3,
id: "ig",
typeId: 3,
name: "Instagram",
},
{
key: 4,
id: "twt",
typeId: 4,
name: "Twitter",
},
{
key: 5,
id: "yt",
typeId: 5,
name: "Youtube",
},
{
key: 6,
id: "emergency",
typeId: 6,
name: "Emergency Issue",
},
{
key: 7,
id: "email",
typeId: 7,
name: "Email",
},
{
key: 8,
id: "inbox",
typeId: 8,
name: "Pesan Masuk",
},
{
key: 9,
id: "whatsapp",
typeId: 9,
name: "Whatssapp",
},
{
key: 10,
id: "tiktok",
typeId: 10,
name: "Tiktok",
},
];
interface RoleData {
id: number;
label: string;
name: string;
value: string;
levelNumber: number;
}
const FormSchema = z.object({
level: z.string({
required_error: "Required",
}),
fullname: z.string({
required_error: "Required",
}),
username: z.string({
required_error: "Required",
}),
role: z.string({
required_error: "Required",
}),
nrp: z.string({
required_error: "Required",
}),
address: z.string({
required_error: "Required",
}),
email: z.string({
required_error: "Required",
}),
phoneNumber: z.string({
required_error: "Required",
}),
password: z.string({
required_error: "Required",
}),
confirmPassword: z.string({
required_error: "Required",
}),
isValidPassword: z.boolean().refine((val) => val === true, {
message: "Check Password",
}),
sns: z.array(z.string()).optional(),
education: z.string().optional(),
school: z.string().optional(),
competency: z.string().optional(),
});
export default function CreateUserForm() {
const router = useRouter();
const MySwal = withReactContent(Swal);
const levelName = getCookiesDecrypt("ulnae");
const [roleList, setRoleList] = useState<RoleData[]>([]);
const [userEducations, setUserEducations] = useState<any>();
const [userSchools, setUserSchools] = useState<any>();
const [userCompetencies, setUserCompetencies] = useState<any>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
password: "",
confirmPassword: "",
sns: [],
education: "1",
school: "4",
competency: "2",
},
});
const passwordVal = form.watch("password");
const confPasswordVal = form.watch("confirmPassword");
const selectedRole = form.watch("role");
useEffect(() => {
initFetch();
getDataAdditional();
}, []);
const initFetch = async () => {
const response = await AdministrationLevelList();
const res = response?.data?.data;
var levelsArr: RoleData[] = [];
res.forEach((levels: RoleData) => {
levelsArr.push({
id: levels.id,
label: levels.name,
name: levels.name,
value: String(levels.id),
levelNumber: levels.levelNumber,
});
});
setRoleList(levelsArr);
};
async function getDataAdditional() {
const resEducations = await getListEducation();
setUserEducations(resEducations?.data?.data);
const resSchools = await getListSchools();
setUserSchools(resSchools?.data?.data);
const resCompetencies = await getListCompetencies();
setUserCompetencies(resCompetencies?.data?.data);
}
const roles =
levelName == "MABES POLRI"
? [
{
id: "ADM-ID",
name: "Admin",
},
{
id: "APP-ID",
name: "Approver",
},
{
id: "CON-ID",
name: "Kontributor",
},
{
id: "SPV-ID",
name: "Supervisor Feedback Center",
},
{
id: "OPT-ID",
name: "Operator Feedback Center",
},
{
id: "KKUR-ID",
name: "Koor Kurator",
},
{
id: "KUR-ID",
name: "Kurator",
},
]
: [
{
id: "APP-ID",
name: "Approver",
},
{
id: "CON-ID",
name: "Kontributor",
},
];
async function save(data: z.infer<typeof FormSchema>) {
let req: any = {
firstName: data.fullname,
username: data.username,
roleId: data.role,
userLevelId: Number(data.level),
memberIdentity: data.nrp,
address: data.address,
email: data.email,
phoneNumber: data.phoneNumber,
password: data.password,
passwordConf: data.confirmPassword,
isDefault: false,
};
if (data.role == "OPT-ID") {
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
}
if (data.role == "KUR-ID") {
req.userEducationId = Number(data.education);
req.userSchoolsId = Number(data.school);
req.userCompetencyId = Number(data.competency);
}
loading();
const response = await saveUserInternal(req);
if (response?.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "Oke",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");
}
});
return false;
}
async function onSubmit(data: z.infer<typeof FormSchema>) {
MySwal.fire({
title: "Simpan Data?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
return (
<div>
<SiteBreadcrumb />
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 bg-white p-10 w-full"
>
<p className="text-xl">Data Pengelola Media Hub</p>
<FormField
control={form.control}
name="level"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Level</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[400px] justify-between",
!field.value && "text-muted-foreground"
)}
>
{field.value
? roleList.find((role) => role.value === field.value)
?.label
: "Pilih level"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput />
<CommandList>
<CommandEmpty>No role found.</CommandEmpty>
<CommandGroup>
{roleList.map((role) => (
<CommandItem
value={role.label}
key={role.value}
onSelect={() => {
form.setValue("level", role.value);
}}
>
{role.label}
<Check
className={cn(
"ml-auto",
role.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="fullname"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Lengkap</FormLabel>
<FormControl>
<Input
placeholder="Masukkan nama lengkap"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Masukkan username"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Role</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex flex-wrap gap-3 w-1/2"
>
{roles.map((role) => (
<FormItem
key={role.id}
className="flex items-center space-x-3 space-y-0"
>
<FormControl>
<RadioGroupItem value={role.id} />
</FormControl>
<FormLabel className="font-normal">
{role.name}
</FormLabel>
</FormItem>
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{selectedRole === "OPT-ID" && (
<FormField
control={form.control}
name="sns"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel>Social Media Yang Ditangani</FormLabel>
</div>
<div className="grid grid-cols-5 gap-2 w-1/2">
{sns.map((item) => (
<FormField
key={item.id}
control={form.control}
name="sns"
render={({ field }) => {
return (
<FormItem
key={item.typeId}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={field.value?.includes(
String(item.typeId)
)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...(field.value || []),
String(item.typeId),
])
: field.onChange(
(field.value || []).filter(
(value) =>
value !== String(item.typeId)
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
)}
{selectedRole === "KUR-ID" && (
<>
<FormField
control={form.control}
name="education"
render={({ field }) => (
<FormItem>
<FormLabel>Pendidikan Terakhir</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userEducations?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="school"
render={({ field }) => (
<FormItem>
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userSchools?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="competency"
render={({ field }) => (
<FormItem>
<FormLabel>Kompetensi</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userCompetencies?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</>
)}
<FormField
control={form.control}
name="nrp"
render={({ field }) => (
<FormItem>
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan NRP"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Alamat</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan alamat"
className="resize-none w-1/2"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="Masukkan email"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>No. Handphone</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor handphone"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Masukkan kata sandi"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="confirmPassword"
render={({ field }) => (
<FormItem>
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Masukkan kata sandi"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<PasswordChecklist
rules={["minLength", "specialChar", "number", "capital", "match"]}
minLength={8}
value={passwordVal}
valueAgain={confPasswordVal}
onChange={(isValid) => {
form.setValue("isValidPassword", isValid);
}}
className="text-sm"
messages={{
minLength: "Password harus lebih dari 8 karakter",
specialChar: "Password harus memiliki spesial karakter",
number: "Password harus memiliki angka",
capital: "Password harus memiliki huruf kapital",
match: "Password sama",
}}
/>
<Link href="/admin/management-user">
<Button type="button" color="primary" variant="outline">
Back
</Button>
</Link>
<Button type="submit" color="primary" className="mx-3">
Submit
</Button>
</form>
</Form>
</div>
);
}

View File

@ -0,0 +1,743 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { zodResolver } from "@hookform/resolvers/zod";
import { Check, ChevronsUpDown } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
AdministrationLevelList,
getListCompetencies,
getListEducation,
getListSchools,
getUserById,
saveUserInternal,
} from "@/service/management-user/management-user";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Link, useRouter } from "@/i18n/routing";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import dynamic from "next/dynamic";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { useParams } from "next/navigation";
const sns = [
{
key: 1,
id: "comment",
typeId: 1,
name: "Komentar Konten",
},
{
key: 2,
id: "fb",
typeId: 2,
name: "Facebook",
},
{
key: 3,
id: "ig",
typeId: 3,
name: "Instagram",
},
{
key: 4,
id: "twt",
typeId: 4,
name: "Twitter",
},
{
key: 5,
id: "yt",
typeId: 5,
name: "Youtube",
},
{
key: 6,
id: "emergency",
typeId: 6,
name: "Emergency Issue",
},
{
key: 7,
id: "email",
typeId: 7,
name: "Email",
},
{
key: 8,
id: "inbox",
typeId: 8,
name: "Pesan Masuk",
},
{
key: 9,
id: "whatsapp",
typeId: 9,
name: "Whatssapp",
},
{
key: 10,
id: "tiktok",
typeId: 10,
name: "Tiktok",
},
];
interface RoleData {
id: number;
label: string;
name: string;
value: string;
levelNumber: number;
}
const FormSchema = z.object({
level: z.string({
required_error: "Required",
}),
fullname: z.string({
required_error: "Required",
}),
username: z.string({
required_error: "Required",
}),
role: z.string({
required_error: "Required",
}),
nrp: z.string({
required_error: "Required",
}),
address: z.string({
required_error: "Required",
}),
email: z.string({
required_error: "Required",
}),
phoneNumber: z.string({
required_error: "Required",
}),
password: z.string({
required_error: "Required",
}),
confirmPassword: z.string({
required_error: "Required",
}),
isValidPassword: z.boolean().refine((val) => val === true, {
message: "Check Password",
}),
sns: z.array(z.string()).optional(),
education: z.string().optional(),
school: z.string().optional(),
competency: z.string().optional(),
});
export default function DetailUserForm() {
const router = useRouter();
const params = useParams();
const id = params?.id;
const MySwal = withReactContent(Swal);
const levelName = getCookiesDecrypt("ulnae");
const [roleList, setRoleList] = useState<RoleData[]>([]);
const [userEducations, setUserEducations] = useState<any>();
const [userSchools, setUserSchools] = useState<any>();
const [userCompetencies, setUserCompetencies] = useState<any>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
password: "",
confirmPassword: "",
sns: [],
education: "1",
school: "4",
competency: "2",
},
});
const passwordVal = form.watch("password");
const confPasswordVal = form.watch("confirmPassword");
const selectedRole = form.watch("role");
useEffect(() => {
getDataAdditional();
initData();
}, []);
const initData = async () => {
loading();
const response = await getUserById(String(id));
const res = response?.data?.data;
close();
console.log("res", res);
if (Number(res.roleId) > 4) {
form.setValue("fullname", res?.fullname);
form.setValue("username", res?.username);
form.setValue("phoneNumber", res?.phoneNumber);
form.setValue("nrp", res?.memberIdentity);
form.setValue("address", res?.address);
form.setValue("email", res?.email);
form.setValue("role", res?.role?.code);
form.setValue("level", String(res?.userLevelId));
} else {
initFetch();
console.log("sadad", res?.role?.code);
form.setValue("fullname", res?.fullname);
form.setValue("username", res?.username);
form.setValue("phoneNumber", res?.phoneNumber);
form.setValue("nrp", res?.memberIdentity);
form.setValue("address", res?.address);
form.setValue("email", res?.email);
form.setValue("role", res?.role?.code);
form.setValue("level", String(res?.userLevelId));
}
};
const initFetch = async () => {
const response = await AdministrationLevelList();
const res = response?.data?.data;
var levelsArr: RoleData[] = [];
res.forEach((levels: RoleData) => {
levelsArr.push({
id: levels.id,
label: levels.name,
name: levels.name,
value: String(levels.id),
levelNumber: levels.levelNumber,
});
});
setRoleList(levelsArr);
};
async function getDataAdditional() {
const resEducations = await getListEducation();
setUserEducations(resEducations?.data?.data);
const resSchools = await getListSchools();
setUserSchools(resSchools?.data?.data);
const resCompetencies = await getListCompetencies();
setUserCompetencies(resCompetencies?.data?.data);
}
const roles =
levelName == "MABES POLRI"
? [
{
id: "ADM-ID",
name: "Admin",
},
{
id: "APP-ID",
name: "Approver",
},
{
id: "CON-ID",
name: "Kontributor",
},
{
id: "SPV-ID",
name: "Supervisor Feedback Center",
},
{
id: "OPT-ID",
name: "Operator Feedback Center",
},
{
id: "KKUR-ID",
name: "Koor Kurator",
},
{
id: "KUR-ID",
name: "Kurator",
},
]
: [
{
id: "APP-ID",
name: "Approver",
},
{
id: "CON-ID",
name: "Kontributor",
},
];
async function save(data: z.infer<typeof FormSchema>) {
let req: any = {
id: id,
firstName: data.fullname,
username: data.username,
roleId: data.role,
userLevelId: Number(data.level),
memberIdentity: data.nrp,
address: data.address,
email: data.email,
password: data.password,
passwordConf: data.confirmPassword,
isDefault: false,
};
if (data.role == "OPT-ID") {
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
}
if (data.role == "KUR-ID") {
req.userEducationId = Number(data.education);
req.userSchoolsId = Number(data.school);
req.userCompetencyId = Number(data.competency);
}
loading();
const response = await saveUserInternal(req);
if (response?.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
showCancelButton: true,
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");
}
});
return false;
}
async function onSubmit(data: z.infer<typeof FormSchema>) {
MySwal.fire({
title: "Simpan Data?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
return (
<div>
<SiteBreadcrumb />
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 bg-white p-10 w-full"
>
<p className="text-xl">Data Pengelola Media Hub</p>
<FormField
control={form.control}
name="level"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Level</FormLabel>
<Popover>
<PopoverTrigger asChild disabled>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[400px] justify-between",
!field.value && "text-muted-foreground"
)}
>
{field.value
? roleList.find((role) => role.value === field.value)
?.label
: "Pilih level"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput />
<CommandList>
<CommandEmpty>No role found.</CommandEmpty>
<CommandGroup>
{roleList.map((role) => (
<CommandItem
value={role.label}
key={role.value}
onSelect={() => {
form.setValue("level", role.value);
}}
>
{role.label}
<Check
className={cn(
"ml-auto",
role.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="fullname"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Lengkap</FormLabel>
<FormControl>
<Input
placeholder="Masukkan nama lengkap"
{...field}
readOnly
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Masukkan username"
{...field}
readOnly
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Role</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-wrap gap-3 w-1/2"
disabled
>
{roles.map((role) => (
<FormItem
key={role.id}
className="flex items-center space-x-3 space-y-0"
>
<FormControl>
<RadioGroupItem value={role.id} />
</FormControl>
<FormLabel className="font-normal">
{role.name}
</FormLabel>
</FormItem>
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{selectedRole === "OPT-ID" && (
<FormField
control={form.control}
name="sns"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel>Social Media Yang Ditangani</FormLabel>
</div>
<div className="grid grid-cols-5 gap-2 w-1/2">
{sns.map((item) => (
<FormField
key={item.id}
control={form.control}
name="sns"
render={({ field }) => {
return (
<FormItem
key={item.typeId}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
disabled
checked={field.value?.includes(
String(item.typeId)
)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...(field.value || []),
String(item.typeId),
])
: field.onChange(
(field.value || []).filter(
(value) =>
value !== String(item.typeId)
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
)}
{selectedRole === "KUR-ID" && (
<>
<FormField
control={form.control}
name="education"
render={({ field }) => (
<FormItem>
<FormLabel>Pendidikan Terakhir</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
disabled
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userEducations?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="school"
render={({ field }) => (
<FormItem>
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
disabled
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userSchools?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="competency"
render={({ field }) => (
<FormItem>
<FormLabel>Kompetensi</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
disabled
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userCompetencies?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</>
)}
<FormField
control={form.control}
name="nrp"
render={({ field }) => (
<FormItem>
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan NRP"
readOnly
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Alamat</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan alamat"
readOnly
className="resize-none w-1/2"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
readOnly
placeholder="Masukkan email"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>No. Handphone</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor handphone"
{...field}
readOnly
className="w-1/2 mb-2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Link href="/admin/management-user">
<Button type="button" color="primary" variant="outline">
Back
</Button>
</Link>
</form>
</Form>
</div>
);
}

View File

@ -0,0 +1,784 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { zodResolver } from "@hookform/resolvers/zod";
import { Check, ChevronsUpDown } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
AdministrationLevelList,
getListCompetencies,
getListEducation,
getListSchools,
getUserById,
saveUserInternal,
} from "@/service/management-user/management-user";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Link, useRouter } from "@/i18n/routing";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import dynamic from "next/dynamic";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, error, loading } from "@/config/swal";
import { useParams } from "next/navigation";
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
ssr: false,
});
const sns = [
{
key: 1,
id: "comment",
typeId: 1,
name: "Komentar Konten",
},
{
key: 2,
id: "fb",
typeId: 2,
name: "Facebook",
},
{
key: 3,
id: "ig",
typeId: 3,
name: "Instagram",
},
{
key: 4,
id: "twt",
typeId: 4,
name: "Twitter",
},
{
key: 5,
id: "yt",
typeId: 5,
name: "Youtube",
},
{
key: 6,
id: "emergency",
typeId: 6,
name: "Emergency Issue",
},
{
key: 7,
id: "email",
typeId: 7,
name: "Email",
},
{
key: 8,
id: "inbox",
typeId: 8,
name: "Pesan Masuk",
},
{
key: 9,
id: "whatsapp",
typeId: 9,
name: "Whatssapp",
},
{
key: 10,
id: "tiktok",
typeId: 10,
name: "Tiktok",
},
];
interface RoleData {
id: number;
label: string;
name: string;
value: string;
levelNumber: number;
}
const FormSchema = z.object({
level: z.string({
required_error: "Required",
}),
fullname: z.string({
required_error: "Required",
}),
username: z.string({
required_error: "Required",
}),
role: z.string({
required_error: "Required",
}),
nrp: z.string({
required_error: "Required",
}),
address: z.string({
required_error: "Required",
}),
email: z.string({
required_error: "Required",
}),
phoneNumber: z.string({
required_error: "Required",
}),
password: z.string({
required_error: "Required",
}),
confirmPassword: z.string({
required_error: "Required",
}),
isValidPassword: z.boolean().refine((val) => val === true, {
message: "Check Password",
}),
sns: z.array(z.string()).optional(),
education: z.string().optional(),
school: z.string().optional(),
competency: z.string().optional(),
});
export default function EditUserForm() {
const router = useRouter();
const params = useParams();
const id = params?.id;
const MySwal = withReactContent(Swal);
const levelName = getCookiesDecrypt("ulnae");
const [roleList, setRoleList] = useState<RoleData[]>([]);
const [userEducations, setUserEducations] = useState<any>();
const [userSchools, setUserSchools] = useState<any>();
const [userCompetencies, setUserCompetencies] = useState<any>();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
password: "",
confirmPassword: "",
sns: [],
education: "1",
school: "4",
competency: "2",
},
});
const passwordVal = form.watch("password");
const confPasswordVal = form.watch("confirmPassword");
const selectedRole = form.watch("role");
useEffect(() => {
getDataAdditional();
initData();
}, []);
const initData = async () => {
loading();
const response = await getUserById(String(id));
const res = response?.data?.data;
close();
console.log("res", res);
if (Number(res.roleId) > 4) {
form.setValue("fullname", res?.fullname);
form.setValue("username", res?.username);
form.setValue("phoneNumber", res?.phoneNumber);
form.setValue("nrp", res?.memberIdentity);
form.setValue("address", res?.address);
form.setValue("email", res?.email);
form.setValue("role", res?.role?.code);
form.setValue("level", String(res?.userLevelId));
} else {
initFetch();
console.log("sadad", res?.role?.code);
form.setValue("fullname", res?.fullname);
form.setValue("username", res?.username);
form.setValue("phoneNumber", res?.phoneNumber);
form.setValue("nrp", res?.memberIdentity);
form.setValue("address", res?.address);
form.setValue("email", res?.email);
form.setValue("role", res?.role?.code);
form.setValue("level", String(res?.userLevelId));
}
};
const initFetch = async () => {
const response = await AdministrationLevelList();
const res = response?.data?.data;
var levelsArr: RoleData[] = [];
res.forEach((levels: RoleData) => {
levelsArr.push({
id: levels.id,
label: levels.name,
name: levels.name,
value: String(levels.id),
levelNumber: levels.levelNumber,
});
});
setRoleList(levelsArr);
};
async function getDataAdditional() {
const resEducations = await getListEducation();
setUserEducations(resEducations?.data?.data);
const resSchools = await getListSchools();
setUserSchools(resSchools?.data?.data);
const resCompetencies = await getListCompetencies();
setUserCompetencies(resCompetencies?.data?.data);
}
const roles =
levelName == "MABES POLRI"
? [
{
id: "ADM-ID",
name: "Admin",
},
{
id: "APP-ID",
name: "Approver",
},
{
id: "CON-ID",
name: "Kontributor",
},
{
id: "SPV-ID",
name: "Supervisor Feedback Center",
},
{
id: "OPT-ID",
name: "Operator Feedback Center",
},
{
id: "KKUR-ID",
name: "Koor Kurator",
},
{
id: "KUR-ID",
name: "Kurator",
},
]
: [
{
id: "APP-ID",
name: "Approver",
},
{
id: "CON-ID",
name: "Kontributor",
},
];
async function save(data: z.infer<typeof FormSchema>) {
let req: any = {
id: id,
firstName: data.fullname,
username: data.username,
roleId: data.role,
userLevelId: Number(data.level),
memberIdentity: data.nrp,
address: data.address,
email: data.email,
password: data.password,
passwordConf: data.confirmPassword,
isDefault: false,
};
if (data.role == "OPT-ID") {
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
}
if (data.role == "KUR-ID") {
req.userEducationId = Number(data.education);
req.userSchoolsId = Number(data.school);
req.userCompetencyId = Number(data.competency);
}
loading();
const response = await saveUserInternal(req);
if (response?.error) {
error(response.message);
return false;
}
close();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "Oke",
}).then((result) => {
if (result.isConfirmed) {
router.push("/admin/management-user");
}
});
return false;
}
async function onSubmit(data: z.infer<typeof FormSchema>) {
MySwal.fire({
title: "Simpan Data?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
return (
<div>
<SiteBreadcrumb />
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 bg-white p-10 w-full"
>
<p className="text-xl">Data Pengelola Media Hub</p>
<FormField
control={form.control}
name="level"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Level</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[400px] justify-between",
!field.value && "text-muted-foreground"
)}
>
{field.value
? roleList.find((role) => role.value === field.value)
?.label
: "Pilih level"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput />
<CommandList>
<CommandEmpty>No role found.</CommandEmpty>
<CommandGroup>
{roleList.map((role) => (
<CommandItem
value={role.label}
key={role.value}
onSelect={() => {
form.setValue("level", role.value);
}}
>
{role.label}
<Check
className={cn(
"ml-auto",
role.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="fullname"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Lengkap</FormLabel>
<FormControl>
<Input
placeholder="Masukkan nama lengkap"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
placeholder="Masukkan username"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Role</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-wrap gap-3 w-1/2"
>
{roles.map((role) => (
<FormItem
key={role.id}
className="flex items-center space-x-3 space-y-0"
>
<FormControl>
<RadioGroupItem value={role.id} />
</FormControl>
<FormLabel className="font-normal">
{role.name}
</FormLabel>
</FormItem>
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{selectedRole === "OPT-ID" && (
<FormField
control={form.control}
name="sns"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel>Social Media Yang Ditangani</FormLabel>
</div>
<div className="grid grid-cols-5 gap-2 w-1/2">
{sns.map((item) => (
<FormField
key={item.id}
control={form.control}
name="sns"
render={({ field }) => {
return (
<FormItem
key={item.typeId}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={field.value?.includes(
String(item.typeId)
)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...(field.value || []),
String(item.typeId),
])
: field.onChange(
(field.value || []).filter(
(value) =>
value !== String(item.typeId)
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}
</FormLabel>
</FormItem>
);
}}
/>
))}
</div>
<FormMessage />
</FormItem>
)}
/>
)}
{selectedRole === "KUR-ID" && (
<>
<FormField
control={form.control}
name="education"
render={({ field }) => (
<FormItem>
<FormLabel>Pendidikan Terakhir</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userEducations?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="school"
render={({ field }) => (
<FormItem>
<FormLabel>Universitas / Perguruan Tinggi</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userSchools?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="competency"
render={({ field }) => (
<FormItem>
<FormLabel>Kompetensi</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{userCompetencies?.map((edu: any) => (
<SelectItem key={edu.id} value={String(edu.id)}>
{edu.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</>
)}
<FormField
control={form.control}
name="nrp"
render={({ field }) => (
<FormItem>
<FormLabel>Nomor Regitrasi Polri {`(NRP)`}</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan NRP"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Alamat</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan alamat"
className="resize-none w-1/2"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="Masukkan email"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>No. Handphone</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Masukkan nomor handphone"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Masukkan kata sandi"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="confirmPassword"
render={({ field }) => (
<FormItem>
<FormLabel>Konfirmasi Kata Sandi</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Masukkan kata sandi"
{...field}
className="w-1/2"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<PasswordChecklist
rules={["minLength", "specialChar", "number", "capital", "match"]}
minLength={8}
value={passwordVal}
valueAgain={confPasswordVal}
onChange={(isValid) => {
form.setValue("isValidPassword", isValid);
}}
className="text-sm"
messages={{
minLength: "Password harus lebih dari 8 karakter",
specialChar: "Password harus memiliki spesial karakter",
number: "Password harus memiliki angka",
capital: "Password harus memiliki huruf kapital",
match: "Password sama",
}}
/>
<Link href="/admin/management-user">
<Button type="button" color="primary" variant="outline">
Back
</Button>
</Link>
<Button type="submit" color="primary" className="mx-3">
Submit
</Button>
</form>
</Form>
</div>
);
}

View File

@ -1,10 +1,69 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import UserExternalTable from "@/components/table/management-user/management-user-external-table";
import UserInternalTable from "@/components/table/management-user/management-user-internal-table";
import InternalTable from "@/components/table/management-user/management-user-internal-table";
import { Button } from "@/components/ui/button";
import DashboardVisualization from "@/components/visualization/dashboard-viz";
import ManagementUserVisualization from "@/components/visualization/management-user-viz";
import { Link, useRouter } from "@/i18n/routing";
import { PlusIcon } from "lucide-react";
import { useEffect, useState } from "react";
export default function ManagementUser() {
const [isInternal, setIsInternal] = useState(true);
const router = useRouter();
useEffect(() => {
router.push("?page=1");
}, [isInternal]);
return (
<div>
<SiteBreadcrumb />
<p className="font-semibold">STATISTIK JUMLAH PENGGUNA</p>
<section id="viz">
<ManagementUserVisualization />
</section>
<section
id="table"
className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5"
>
<div className="flex justify-between py-3">
<p className="text-lg">
Data User {isInternal ? "Internal" : "Eksternal"}
</p>
{isInternal && (
<Link href="/admin/management-user/internal/create">
<Button color="primary" size="md">
<PlusIcon />
Add User
</Button>
</Link>
)}
</div>
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button
rounded="md"
onClick={() => setIsInternal(true)}
className={` hover:text-white
${
!isInternal ? "bg-white text-black " : "bg-black text-white "
}`}
>
User Internal
</Button>
<Button
rounded="md"
onClick={() => setIsInternal(false)}
className={`hover:text-white ${
!isInternal ? "bg-black text-white " : "bg-white text-black "
}
`}
>
User Eksternal
</Button>
</div>
{isInternal ? <UserInternalTable /> : <UserExternalTable />}
</section>
</div>
);
}

View File

@ -4,6 +4,7 @@ import ContentListTable from "./component/table";
import { useState } from "react";
import BannerListTable from "./component/banner-table";
import { Button } from "@/components/ui/button";
export default function AdminBanner() {
const [selectedTab, setSelectedTab] = useState("content");
@ -17,23 +18,31 @@ export default function AdminBanner() {
? "Daftar List Media"
: "Table List Banner"}
<div className="flex flex-row text-sm">
<a
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button
rounded="md"
onClick={() => setSelectedTab("content")}
className={`px-3 py-2 cursor-pointer ${
selectedTab === "content" ? "bg-primary text-white" : ""
} `}
className={` hover:text-white
${
selectedTab === "content"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Kontent
</a>
<a
</Button>
<Button
rounded="md"
onClick={() => setSelectedTab("banner")}
className={`px-3 py-2 cursor-pointer ${
selectedTab === "banner" ? "bg-primary text-white" : ""
} `}
className={` hover:text-white
${
selectedTab === "banner"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Banner
</a>
</Button>
</div>
</div>

View File

@ -103,8 +103,15 @@ const columns: ColumnDef<any>[] = [
</Button>
</MenubarTrigger>
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
<EditCategoryModal id={row.original.id} isDetail={true} />
<EditCategoryModal id={row.original.id} />
<EditCategoryModal
id={row.original.id}
isDetail={true}
thumbnailLink={row.original.thumbnailLink}
/>
<EditCategoryModal
id={row.original.id}
thumbnailLink={row.original.thumbnailLink}
/>
<a
onClick={() => categoryDelete(row.original.id)}
className="hover:underline cursor-pointer hover:text-destructive"

View File

@ -105,15 +105,15 @@ const publishToList = [
export default function EditCategoryModal(props: {
id: string;
thumbnailLink: string;
isDetail?: boolean;
}) {
const { id, isDetail } = props;
const { id, isDetail, thumbnailLink } = props;
const [files, setFiles] = useState<File[]>([]);
const [isOpen, setIsOpen] = useState(false);
const { toast } = useToast();
const router = useRouter();
const [initDataUnit, setInitDataUnit] = useState<string[]>([]);
const [satkerData, setSatkerData] = useState<string[]>([]);
const [unitData, setUnitData] = useState<string[]>([]);
const [userList, setUserList] = useState<
@ -146,6 +146,7 @@ export default function EditCategoryModal(props: {
removeAndReturn(data?.publishedFor, [2, 3, 4])
);
form.setValue("publishTo", data?.publishedLocation?.split(","));
form.setValue("file", thumbnailLink);
setUnitData(filterString(data?.publishedLocationLevel, "under"));
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
@ -163,15 +164,15 @@ export default function EditCategoryModal(props: {
}
function filterString(inputString: string, type: string) {
const numbers = inputString.split(",").map(Number);
const numbers = inputString?.split(",").map(Number);
if (type === "above") {
const above700 = numbers.filter((num) => num > 700);
const above700 = numbers?.filter((num) => num > 700);
return above700.map(String);
return above700?.map(String);
} else {
const under700 = numbers.filter((num) => num < 700);
const under700 = numbers?.filter((num) => num < 700);
return under700.map(String);
return under700?.map(String);
}
}
@ -226,9 +227,11 @@ export default function EditCategoryModal(props: {
formMedia.append("description", data.description);
formMedia.append("mediaTypes", data.contentType.join(","));
formMedia.append("publishedFor", data.selectedUser.join(","));
formMedia.append("file", files[0]);
formMedia.append("publishedLocation", data.publishTo.sort().join(","));
formMedia.append("publishedLocationLevel", removeDuplicates(join));
if (files?.length > 0) {
formMedia.append("file", files[0]);
}
const response = await postCategory(formMedia);
close();
@ -253,7 +256,10 @@ export default function EditCategoryModal(props: {
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger>
<a onClick={() => setIsOpen(true)} className="hover:underline">
<a
onClick={() => setIsOpen(true)}
className="hover:underline cursor-pointer"
>
{isDetail ? "Detail" : "Edit"}
</a>
</DialogTrigger>
@ -520,50 +526,71 @@ export default function EditCategoryModal(props: {
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="file"
render={({ field }) => (
<FormItem>
<FormLabel>Thumbnail Category</FormLabel>
{files.length < 1 && (
<Fragment>
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
<CloudUpload className="text-default-300 w-10 h-10" />
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
Tarik file disini atau klik untuk upload.
</h4>
<div className=" text-xs text-muted-foreground">
( Upload file dengan format .jpg, .jpeg, atau .png.
Ukuran maksimal 100mb.)
{isDetail ? (
<div className="flex flex-row gap-2">
<img src={thumbnailLink} className="w-[30%]" alt="thumbnail" />
</div>
) : (
<FormField
control={form.control}
name="file"
render={({ field }) => (
<FormItem>
<FormLabel>Thumbnail Category</FormLabel>
{files.length < 1 && field.value === "" && (
<Fragment>
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
<CloudUpload className="text-default-300 w-10 h-10" />
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
Tarik file disini atau klik untuk upload.
</h4>
<div className=" text-xs text-muted-foreground">
( Upload file dengan format .jpg, .jpeg, atau
.png. Ukuran maksimal 100mb.)
</div>
</div>
</div>
</Fragment>
)}
{field.value !== "" && (
<div className="flex flex-row gap-2">
<img
src={field.value}
className="w-[30%]"
alt="thumbnail"
/>
<a
onClick={() => form.setValue("file", "")}
className="cursor-pointer"
>
<Icon icon="fa-solid:times" color="red" />
</a>
</div>
</Fragment>
)}
)}
{files.length > 0 && (
<div className="flex flex-row gap-2">
<img
src={URL.createObjectURL(files[0])}
className="w-[30%]"
alt="thumbnail"
/>
<a
onClick={() => handleRemoveFile(files[0])}
className="cursor-pointer"
>
<Icon icon="fa-solid:times" color="red" />
</a>
</div>
)}
{files.length > 0 && (
<div className="flex flex-row gap-2">
<img
src={URL.createObjectURL(files[0])}
className="w-[30%]"
alt="thumbnail"
/>
<a
onClick={() => handleRemoveFile(files[0])}
className="cursor-pointer"
>
<Icon icon="fa-solid:times" color="red" />
</a>
</div>
)}
<FormMessage />
</FormItem>
)}
/> */}
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
@ -584,16 +611,18 @@ export default function EditCategoryModal(props: {
</FormItem>
)}
/>
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Edit Kategori
</Button>
</DialogFooter>
{!isDetail && (
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Edit Kategori
</Button>
</DialogFooter>
)}
</form>
</Form>
</DialogContent>

View File

@ -0,0 +1,112 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Link, useRouter } from "@/i18n/routing";
import { error } from "@/config/swal";
import { deleteCategory, deleteDataFAQ } from "@/service/settings/settings";
import { useToast } from "@/components/ui/use-toast";
// import EditCategoryModal from "./edit";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Menubar,
MenubarContent,
MenubarMenu,
MenubarTrigger,
} from "@/components/ui/menubar";
import { htmlToString } from "@/utils/globals";
import EditFAQModal from "./edit";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "question",
header: "Pertanyaan",
cell: ({ row }) => (
<span className="normal-case">
{htmlToString(row.getValue("question"))}
</span>
),
},
{
accessorKey: "answer",
header: "Answer",
cell: ({ row }) => (
<span className="normal-case">
{htmlToString(row.getValue("answer"))}
</span>
),
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const { toast } = useToast();
const faqDelete = async (id: string) => {
const response = await deleteDataFAQ(id);
console.log(response);
if (response?.error) {
error(response.message);
return false;
}
toast({
title: "Sukses",
description: "Berhasil Delete",
});
router.push("/admin/settings/faq?dataChange=true");
};
return (
<Menubar className="border-none">
<MenubarMenu>
<MenubarTrigger>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</MenubarTrigger>
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
<EditFAQModal id={row.original.id} isDetail={true} />
<EditFAQModal id={row.original.id} isDetail={false} />
<a
onClick={() => faqDelete(row.original.id)}
className="hover:underline cursor-pointer hover:text-destructive"
>
Delete
</a>
</MenubarContent>
</MenubarMenu>
</Menubar>
);
},
},
];
export default columns;

View File

@ -0,0 +1,299 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
getUserRoles,
postCategory,
postDataFAQ,
} from "@/service/settings/settings";
import { Fragment, useEffect, useState } from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Icon } from "@iconify/react/dist/iconify.js";
import { Textarea } from "@/components/ui/textarea";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
import { useDropzone } from "react-dropzone";
import { CloudUpload } from "lucide-react";
import Image from "next/image";
import { Upload } from "tus-js-client";
const FormSchema = z.object({
answer: z.string({
required_error: "Required",
}),
question: z.string({
required_error: "Required",
}),
publishTo: z.string({
required_error: "Required",
}),
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
// message: "Required",
// }),
});
const publishToList = [
{
id: "mabes",
name: "Nasional",
},
{
id: "polda",
name: "Polda",
},
{
id: "satker",
name: "Satker",
},
{
id: "internasional",
name: "Internasional",
},
];
export default function CreateFAQModal() {
const router = useRouter();
const { toast } = useToast();
const [isOpen, setIsOpen] = useState(false);
const [satkerData, setSatkerData] = useState<string[]>([]);
const [unitData, setUnitData] = useState<string[]>([]);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: { publishTo: "wilayah" },
});
const target = form.watch("publishTo");
const isAllTargetChecked = publishToList.every((item) =>
target?.includes(item.id)
);
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
const request = {
question: data.question,
answer: data.answer,
isInternational: data.publishTo === "wilayah" ? false : true,
isActive: true,
};
const response = await postDataFAQ(request);
close();
if (response?.error) {
toast({ title: stringify(response.message), variant: "destructive" });
return false;
}
toast({
title: "Succes",
description: "FAQ berhasil dibuat",
});
router.push("/admin/settings/faq?dataChange=true");
setIsOpen(false);
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button color="primary" size="md">
Tambah FAQ
</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Tambah FAQ</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3 bg-white rounded-sm"
>
<FormField
control={form.control}
name="publishTo"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Wilayah Publish</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-row space-x-1"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="wilayah" />
</FormControl>
<FormLabel className="font-normal">Wilayah</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="international" />
</FormControl>
<FormLabel className="font-normal">
Internasional
</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="publishTo"
render={() => (
<FormItem>
<FormLabel>Wilayah Publish</FormLabel>
<div className="flex flex-row items-center gap-2">
<div className="flex gap-3 items-center">
<Checkbox
id="all"
checked={isAllTargetChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"publishTo",
publishToList.map((item) => item.id)
);
} else {
form.setValue("publishTo", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{publishToList.map((item) => (
<>
<FormField
key={item.id}
control={form.control}
name="publishTo"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start "
>
<div className="flex items-center gap-3">
<FormControl>
<Checkbox
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...field.value,
item.id,
])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}{" "}
</FormLabel>
</div>
</FormItem>
);
}}
/>
{item.id === "polda" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
unit="Polda"
isDetail={false}
sendDataToParent={(data) => setUnitData(data)}
/>
)}
{item.id === "satker" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
isDetail={false}
unit="Satker"
sendDataToParent={(data) => setSatkerData(data)}
/>
)}
</>
))}
</div>
<FormMessage />
</FormItem>
)}
/> */}
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
<FormLabel>Pertanyaan</FormLabel>
<FormControl>
<Textarea placeholder="Masukkan pertanyaan" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="answer"
render={({ field }) => (
<FormItem>
<FormLabel>Jawaban</FormLabel>
<FormControl>
<Textarea placeholder="Masukkan jawaban" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Tambah FAQ
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,325 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
detailDataFAQ,
getUserRoles,
postCategory,
postDataFAQ,
} from "@/service/settings/settings";
import { Fragment, useEffect, useState } from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Icon } from "@iconify/react/dist/iconify.js";
import { Textarea } from "@/components/ui/textarea";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
import { useDropzone } from "react-dropzone";
import { CloudUpload } from "lucide-react";
import Image from "next/image";
import { Upload } from "tus-js-client";
import { id } from "date-fns/locale";
import { htmlToString } from "@/utils/globals";
const FormSchema = z.object({
answer: z.string({
required_error: "Required",
}),
question: z.string({
required_error: "Required",
}),
publishTo: z.string({
required_error: "Required",
}),
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
// message: "Required",
// }),
});
const publishToList = [
{
id: "mabes",
name: "Nasional",
},
{
id: "polda",
name: "Polda",
},
{
id: "satker",
name: "Satker",
},
{
id: "internasional",
name: "Internasional",
},
];
export default function EditFAQModal(props: { id: string; isDetail: boolean }) {
const { id, isDetail } = props;
const router = useRouter();
const { toast } = useToast();
const [isOpen, setIsOpen] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});
useEffect(() => {
initState();
}, [id]);
const initState = async () => {
const res = await detailDataFAQ(id);
const data = res?.data?.data;
form.setValue("question", htmlToString(data.question));
form.setValue("answer", htmlToString(data.answer));
form.setValue(
"publishTo",
data.isInternational ? "international" : "wilayah"
);
};
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
const request = {
id: Number(id),
question: data.question,
answer: data.answer,
isInternational: data.publishTo === "wilayah" ? false : true,
isActive: true,
};
const response = await postDataFAQ(request);
close();
if (response?.error) {
toast({ title: stringify(response.message), variant: "destructive" });
return false;
}
toast({
title: "Succes",
description: "FAQ berhasil diubah",
});
router.push("/admin/settings/faq?dataChange=true");
setIsOpen(false);
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<a className="hover:underline cursor-pointer">
{isDetail ? "Detail" : "Edit"}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>{isDetail ? "Detail" : "Edit"} FAQ</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3 bg-white rounded-sm"
>
<FormField
control={form.control}
name="publishTo"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Wilayah Publish</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-row space-x-1"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="wilayah" />
</FormControl>
<FormLabel className="font-normal">Wilayah</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="international" />
</FormControl>
<FormLabel className="font-normal">
Internasional
</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="publishTo"
render={() => (
<FormItem>
<FormLabel>Wilayah Publish</FormLabel>
<div className="flex flex-row items-center gap-2">
<div className="flex gap-3 items-center">
<Checkbox
id="all"
disabled={isDetail}
checked={isAllTargetChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"publishTo",
publishToList.map((item) => item.id)
);
} else {
form.setValue("publishTo", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{publishToList.map((item) => (
<>
<FormField
key={item.id}
control={form.control}
name="publishTo"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start "
>
<div className="flex items-center gap-3">
<FormControl>
<Checkbox
disabled={isDetail}
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...field.value,
item.id,
])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}{" "}
</FormLabel>
</div>
</FormItem>
);
}}
/>
{item.id === "polda" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
unit="Polda"
isDetail={isDetail}
sendDataToParent={(data) => setUnitData(data)}
/>
)}
{item.id === "satker" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
isDetail={isDetail}
unit="Satker"
sendDataToParent={(data) => setSatkerData(data)}
/>
)}
</>
))}
</div>
<FormMessage />
</FormItem>
)}
/> */}
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
<FormLabel>Pertanyaan</FormLabel>
<FormControl>
<Textarea
readOnly={isDetail}
placeholder="Masukkan pertanyaan"
value={field.value}
onChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="answer"
render={({ field }) => (
<FormItem>
<FormLabel>Jawaban</FormLabel>
<FormControl>
<Textarea
readOnly={isDetail}
placeholder="Masukkan jawaban"
value={field.value}
onChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{!isDetail && (
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Edit FAQ
</Button>
</DialogFooter>
)}
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,184 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link, useRouter } from "@/i18n/routing";
import { NewCampaignIcon } from "@/components/icon";
import { getCategories, getListFAQ } from "@/service/settings/settings";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import CreateFAQModal from "./create";
const AdminFAQTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [openModal, setOpenModal] = React.useState(false);
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
React.useEffect(() => {
if (dataChange) {
router.push("/admin/settings/faq");
}
fetchData();
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
React.useEffect(() => {
fetchData();
}, [page]);
async function fetchData() {
try {
loading();
const response = await getListFAQ();
const data = response?.data?.data;
console.log("respone", response);
data.forEach((item: any, index: number) => {
item.no = (page - 1) * 10 + index + 1;
});
setDataTable(data);
setTotalData(data?.length);
setTotalPage(1);
close();
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">FAQ</p>
<CreateFAQModal />
</div>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
{/* <TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/> */}
</div>
);
};
export default AdminFAQTable;

View File

@ -0,0 +1,11 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import AdminFAQTable from "./component/table";
export default function FAQSetting() {
return (
<div>
<SiteBreadcrumb />
<AdminFAQTable />
</div>
);
}

View File

@ -0,0 +1,121 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Link, useRouter } from "@/i18n/routing";
import { error } from "@/config/swal";
import { deleteCategory, deleteDataFAQ } from "@/service/settings/settings";
import { useToast } from "@/components/ui/use-toast";
// import EditCategoryModal from "./edit";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Menubar,
MenubarContent,
MenubarMenu,
MenubarTrigger,
} from "@/components/ui/menubar";
import { htmlToString } from "@/utils/globals";
import EditFAQModal from "./edit";
import EditFeedbackModal from "./edit";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "question",
header: "Poin Penilaian",
cell: ({ row }) => (
<span className="normal-case">
{htmlToString(row.getValue("question"))}
</span>
),
},
{
accessorKey: "isInternational",
header: "Wilayah Publish",
cell: ({ row }) => (
<span className="normal-case">
{row.getValue("isInternational") ? "Internasional" : "Wilayah"}
</span>
),
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const { toast } = useToast();
const faqDelete = async (id: string) => {
const response = await deleteDataFAQ(id);
console.log(response);
if (response?.error) {
error(response.message);
return false;
}
toast({
title: "Sukses",
description: "Berhasil Delete",
});
router.push("/admin/settings/feedback?dataChange=true");
};
return (
<Menubar className="border-none">
<MenubarMenu>
<MenubarTrigger>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</MenubarTrigger>
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
<EditFeedbackModal
id={row.original.id}
isDetail={true}
data={row.original}
/>
<EditFeedbackModal
id={row.original.id}
isDetail={false}
data={row.original}
/>
<a
onClick={() => faqDelete(row.original.id)}
className="hover:underline cursor-pointer hover:text-destructive"
>
Delete
</a>
</MenubarContent>
</MenubarMenu>
</Menubar>
);
},
},
];
export default columns;

View File

@ -0,0 +1,275 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
getUserRoles,
postCategory,
postDataFAQ,
postDataFeedback,
} from "@/service/settings/settings";
import { Fragment, useEffect, useState } from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Icon } from "@iconify/react/dist/iconify.js";
import { Textarea } from "@/components/ui/textarea";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
import { useDropzone } from "react-dropzone";
import { CloudUpload } from "lucide-react";
import Image from "next/image";
import { Upload } from "tus-js-client";
const FormSchema = z.object({
question: z.string({
required_error: "Required",
}),
publishTo: z.string({
required_error: "Required",
}),
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
// message: "Required",
// }),
});
const publishToList = [
{
id: "mabes",
name: "Nasional",
},
{
id: "polda",
name: "Polda",
},
{
id: "satker",
name: "Satker",
},
{
id: "internasional",
name: "Internasional",
},
];
export default function CreateFAQModal() {
const router = useRouter();
const { toast } = useToast();
const [isOpen, setIsOpen] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: { publishTo: "wilayah" },
});
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
const request = {
question: data.question,
isInternational: data.publishTo === "wilayah" ? false : true,
isActive: true,
};
const response = await postDataFeedback(request);
close();
if (response?.error) {
toast({ title: stringify(response.message), variant: "destructive" });
return false;
}
toast({
title: "Succes",
description: "Feedback berhasil dibuat",
});
router.push("/admin/settings/feedback?dataChange=true");
setIsOpen(false);
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button color="primary" size="md">
Tambah Feedback
</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Tambah Feedback</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3 bg-white rounded-sm"
>
<FormField
control={form.control}
name="publishTo"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Wilayah Publish</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-row space-x-1"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="wilayah" />
</FormControl>
<FormLabel className="font-normal">Wilayah</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="international" />
</FormControl>
<FormLabel className="font-normal">
Internasional
</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="publishTo"
render={() => (
<FormItem>
<FormLabel>Wilayah Publish</FormLabel>
<div className="flex flex-row items-center gap-2">
<div className="flex gap-3 items-center">
<Checkbox
id="all"
checked={isAllTargetChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"publishTo",
publishToList.map((item) => item.id)
);
} else {
form.setValue("publishTo", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{publishToList.map((item) => (
<>
<FormField
key={item.id}
control={form.control}
name="publishTo"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start "
>
<div className="flex items-center gap-3">
<FormControl>
<Checkbox
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...field.value,
item.id,
])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}{" "}
</FormLabel>
</div>
</FormItem>
);
}}
/>
{item.id === "polda" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
unit="Polda"
isDetail={false}
sendDataToParent={(data) => setUnitData(data)}
/>
)}
{item.id === "satker" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
isDetail={false}
unit="Satker"
sendDataToParent={(data) => setSatkerData(data)}
/>
)}
</>
))}
</div>
<FormMessage />
</FormItem>
)}
/> */}
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
<FormLabel>Pertanyaan</FormLabel>
<FormControl>
<Textarea placeholder="Masukkan pertanyaan" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Tambah Feeback
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,311 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
detailDataFAQ,
detailDataFeedback,
getUserRoles,
postCategory,
postDataFAQ,
postDataFeedback,
} from "@/service/settings/settings";
import { Fragment, useEffect, useState } from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Icon } from "@iconify/react/dist/iconify.js";
import { Textarea } from "@/components/ui/textarea";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
import { useDropzone } from "react-dropzone";
import { CloudUpload } from "lucide-react";
import Image from "next/image";
import { Upload } from "tus-js-client";
import { id } from "date-fns/locale";
import { htmlToString } from "@/utils/globals";
const FormSchema = z.object({
question: z.string({
required_error: "Required",
}),
publishTo: z.string({
required_error: "Required",
}),
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
// message: "Required",
// }),
});
const publishToList = [
{
id: "mabes",
name: "Nasional",
},
{
id: "polda",
name: "Polda",
},
{
id: "satker",
name: "Satker",
},
{
id: "internasional",
name: "Internasional",
},
];
export default function EditFeedbackModal(props: {
id: string;
isDetail: boolean;
data: {
id: number;
question: string;
isInternational: boolean;
isActive: boolean;
};
}) {
const { id, isDetail, data } = props;
const router = useRouter();
const { toast } = useToast();
const [isOpen, setIsOpen] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});
useEffect(() => {
initState();
}, [id]);
const initState = async () => {
form.setValue("question", htmlToString(data.question));
form.setValue(
"publishTo",
data.isInternational ? "international" : "wilayah"
);
};
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
const request = {
id: Number(id),
question: data.question,
isInternational: data.publishTo === "wilayah" ? false : true,
isActive: true,
};
const response = await postDataFeedback(request);
close();
if (response?.error) {
toast({ title: stringify(response.message), variant: "destructive" });
return false;
}
toast({
title: "Succes",
description: "Feedback berhasil diubah",
});
router.push("/admin/settings/feedback?dataChange=true");
setIsOpen(false);
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<a className="hover:underline cursor-pointer">
{isDetail ? "Detail" : "Edit"}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>{isDetail ? "Detail" : "Edit"} Feedback</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3 bg-white rounded-sm"
>
<FormField
control={form.control}
name="publishTo"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Wilayah Publish</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex flex-row space-x-1"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="wilayah" />
</FormControl>
<FormLabel className="font-normal">Wilayah</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="international" />
</FormControl>
<FormLabel className="font-normal">
Internasional
</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="publishTo"
render={() => (
<FormItem>
<FormLabel>Wilayah Publish</FormLabel>
<div className="flex flex-row items-center gap-2">
<div className="flex gap-3 items-center">
<Checkbox
id="all"
disabled={isDetail}
checked={isAllTargetChecked}
onCheckedChange={(checked) => {
if (checked) {
form.setValue(
"publishTo",
publishToList.map((item) => item.id)
);
} else {
form.setValue("publishTo", []);
}
}}
/>
<label htmlFor="all" className="text-sm">
Semua
</label>
</div>
{publishToList.map((item) => (
<>
<FormField
key={item.id}
control={form.control}
name="publishTo"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start "
>
<div className="flex items-center gap-3">
<FormControl>
<Checkbox
disabled={isDetail}
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([
...field.value,
item.id,
])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.name}{" "}
</FormLabel>
</div>
</FormItem>
);
}}
/>
{item.id === "polda" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
unit="Polda"
isDetail={isDetail}
sendDataToParent={(data) => setUnitData(data)}
/>
)}
{item.id === "satker" &&
form.getValues("publishTo")?.includes(item.id) && (
<UnitMapping
isDetail={isDetail}
unit="Satker"
sendDataToParent={(data) => setSatkerData(data)}
/>
)}
</>
))}
</div>
<FormMessage />
</FormItem>
)}
/> */}
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
<FormLabel>Pertanyaan</FormLabel>
<FormControl>
<Textarea
readOnly={isDetail}
placeholder="Masukkan pertanyaan"
value={field.value}
onChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{!isDetail && (
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Edit Feedback
</Button>
</DialogFooter>
)}
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,181 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link, useRouter } from "@/i18n/routing";
import { NewCampaignIcon } from "@/components/icon";
import {
getCategories,
getListFAQ,
getListFeedback,
} from "@/service/settings/settings";
import CreateFAQModal from "./create";
const AdminFeedbackTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [openModal, setOpenModal] = React.useState(false);
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
React.useEffect(() => {
if (dataChange) {
router.push("/admin/settings/feedback");
}
fetchData();
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
React.useEffect(() => {
fetchData();
}, [page]);
async function fetchData() {
try {
loading();
const response = await getListFeedback();
const data = response?.data?.data;
console.log("respone", response);
data.forEach((item: any, index: number) => {
item.no = (page - 1) * 10 + index + 1;
});
setDataTable(data);
setTotalData(data?.length);
setTotalPage(1);
close();
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">Feedback</p>
<CreateFAQModal />
</div>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
{/* <TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/> */}
</div>
);
};
export default AdminFeedbackTable;

View File

@ -0,0 +1,11 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import AdminFeedbackTable from "./component/table";
export default function FAQSetting() {
return (
<div>
<SiteBreadcrumb />
<AdminFeedbackTable />
</div>
);
}

View File

@ -19,6 +19,14 @@ import { useEffect, useRef } from "react";
import { getPrivacy, savePrivacy } from "@/service/settings/settings";
import { useToast } from "@/components/ui/use-toast";
import { Button } from "@/components/ui/button";
import dynamic from "next/dynamic";
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
const FormSchema = z.object({
title: z.string({
@ -96,7 +104,7 @@ export default function AdminPrivacyPolicy() {
<FormItem>
<FormLabel>Konten</FormLabel>
<FormControl>
<JoditEditor
{/* <JoditEditor
ref={editor}
value={field.value}
config={{
@ -104,6 +112,11 @@ export default function AdminPrivacyPolicy() {
}}
className="dark:text-black"
onChange={field.onChange}
/> */}
<CustomEditor
onChange={field.onChange}
initialData={field.value}
/>
</FormControl>

View File

@ -0,0 +1,127 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Link, useRouter } from "@/i18n/routing";
import { error } from "@/config/swal";
import { deleteCategory, deleteDataFAQ } from "@/service/settings/settings";
import { useToast } from "@/components/ui/use-toast";
// import EditCategoryModal from "./edit";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Menubar,
MenubarContent,
MenubarMenu,
MenubarTrigger,
} from "@/components/ui/menubar";
import { htmlToString } from "@/utils/globals";
import EditFAQModal from "./edit";
import EditFeedbackModal from "./edit";
import EditTagModal from "./edit";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "tagName",
header: "Nama Tag",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("tagName")}</span>
),
},
{
accessorKey: "categoryName",
header: "Kategori",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("categoryName")}</span>
),
},
{
accessorKey: "contentCount",
header: "Jumlah Content",
cell: ({ row }) => (
<span className="normal-case">
{row.getValue("contentCount") ? row.getValue("contentCount") : "-"}
</span>
),
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
const { toast } = useToast();
const tagDelete = async (id: string) => {
// const response = await deleteDataFAQ(id);
// console.log(response);
// if (response?.error) {
// error(response.message);
// return false;
// }
toast({
title: "Sukses",
description: "Berhasil Delete",
});
router.push("/admin/settings/tag?dataChange=true");
};
return (
<Menubar className="border-none">
<MenubarMenu>
<MenubarTrigger>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</MenubarTrigger>
<MenubarContent className="flex flex-col gap-2 justify-center items-start p-4">
<EditTagModal
id={row.original.id}
isDetail={true}
data={row.original}
/>
<EditTagModal
id={row.original.id}
isDetail={false}
data={row.original}
/>
<a
onClick={() => tagDelete(row.original.id)}
className="hover:underline cursor-pointer hover:text-destructive"
>
Delete
</a>
</MenubarContent>
</MenubarMenu>
</Menubar>
);
},
},
];
export default columns;

View File

@ -0,0 +1,211 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import { Fragment, useEffect, useState } from "react";
import { useToast } from "@/components/ui/use-toast";
import { getCategories, getCategoriesAll } from "@/service/settings/settings";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Check, ChevronsUpDown } from "lucide-react";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { cn } from "@/lib/utils";
const FormSchema = z.object({
name: z.string({
required_error: "Required",
}),
category: z.string({
required_error: "Required",
}),
});
export default function CreateTagModal() {
const router = useRouter();
const { toast } = useToast();
const [categoryList, setCategoryList] = useState<
{ id: number; label: string; value: string }[]
>([]);
const [isOpen, setIsOpen] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
const request = {
tagName: data.name,
categoryId: Number(data.category),
isActive: true,
};
console.log("reqqq", request);
// const response = await postDataFeedback(request);
// close();
// if (response?.error) {
// toast({ title: stringify(response.message), variant: "destructive" });
// return false;
// }
toast({
title: "Succes",
description: "Tag berhasil dibuat",
});
router.push("/admin/settings/tag?dataChange=true");
setIsOpen(false);
};
useEffect(() => {
getCategoryParent();
}, []);
async function getCategoryParent() {
const response = await getCategoriesAll();
const res = response?.data?.data.content;
console.log("res", res);
var levelsArr: { id: number; label: string; value: string }[] = [];
res.forEach((levels: { id: number; name: string }) => {
levelsArr.push({
id: levels.id,
label: levels.name,
value: String(levels.id),
});
});
setCategoryList(levelsArr);
}
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button color="primary" size="md">
Tambah Tag
</Button>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>Tambah Tag</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3 bg-white rounded-sm"
>
<FormField
control={form.control}
name="category"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Category</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[400px] justify-between",
!field.value && "text-muted-foreground"
)}
>
{field.value
? categoryList.find(
(categ) => categ.value === field.value
)?.label
: "Pilih level"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput />
<CommandList>
<CommandEmpty>No role found.</CommandEmpty>
<CommandGroup>
{categoryList.map((role) => (
<CommandItem
value={role.label}
key={role.value}
onSelect={() => {
form.setValue("category", role.value);
}}
>
{role.label}
<Check
className={cn(
"ml-auto",
role.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Tag</FormLabel>
<FormControl>
<Input placeholder="Masukkan nama tag" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Tambah Tag
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,273 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
detailDataFAQ,
detailDataFeedback,
getCategoriesAll,
getUserRoles,
postCategory,
postDataFAQ,
postDataFeedback,
} from "@/service/settings/settings";
import { Fragment, useEffect, useState } from "react";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Check, ChevronsUpDown } from "lucide-react";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { cn } from "@/lib/utils";
const FormSchema = z.object({
name: z.string({
required_error: "Required",
}),
category: z.string({
required_error: "Required",
}),
// publishTo: z.array(z.string()).refine((value) => value.some((item) => item), {
// message: "Required",
// }),
});
const publishToList = [
{
id: "mabes",
name: "Nasional",
},
{
id: "polda",
name: "Polda",
},
{
id: "satker",
name: "Satker",
},
{
id: "internasional",
name: "Internasional",
},
];
export default function EditTagModal(props: {
id: string;
isDetail: boolean;
data: {
id: number;
tagName: string;
categoryId: number;
subCategoryId: number;
};
}) {
const { id, isDetail, data } = props;
const router = useRouter();
const { toast } = useToast();
const [categoryList, setCategoryList] = useState<
{ id: number; label: string; value: string }[]
>([]);
const [isOpen, setIsOpen] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});
useEffect(() => {
initState();
}, [id]);
const initState = async () => {
form.setValue("name", data.tagName);
form.setValue("category", String(data.categoryId));
};
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
const request = {
id: Number(id),
tagName: data.name,
categoryId: Number(data.category),
isActive: true,
};
const response = await postDataFeedback(request);
close();
if (response?.error) {
toast({ title: stringify(response.message), variant: "destructive" });
return false;
}
toast({
title: "Succes",
description: "Tag berhasil diubah",
});
router.push("/admin/settings/tag?dataChange=true");
setIsOpen(false);
};
useEffect(() => {
getCategoryParent();
}, []);
async function getCategoryParent() {
const response = await getCategoriesAll();
const res = response?.data?.data.content;
console.log("res", res);
var levelsArr: { id: number; label: string; value: string }[] = [];
res.forEach((levels: { id: number; name: string }) => {
levelsArr.push({
id: levels.id,
label: levels.name,
value: String(levels.id),
});
});
setCategoryList(levelsArr);
}
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<a className="hover:underline cursor-pointer">
{isDetail ? "Detail" : "Edit"}
</a>
</DialogTrigger>
<DialogContent size="md">
<DialogHeader>
<DialogTitle>{isDetail ? "Detail" : "Edit"} Tag</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3 bg-white rounded-sm"
>
<FormField
control={form.control}
name="category"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Pilih Category</FormLabel>
<Popover>
<PopoverTrigger asChild disabled={isDetail}>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[400px] justify-between",
!field.value && "text-muted-foreground"
)}
>
{field.value
? categoryList.find(
(categ) => categ.value === field.value
)?.label
: "Pilih level"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
<Command>
<CommandInput />
<CommandList>
<CommandEmpty>No role found.</CommandEmpty>
<CommandGroup>
{categoryList.map((role) => (
<CommandItem
value={role.label}
key={role.value}
onSelect={() => {
form.setValue("category", role.value);
}}
>
{role.label}
<Check
className={cn(
"ml-auto",
role.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Nama Tag</FormLabel>
<FormControl>
<Input
readOnly={isDetail}
placeholder="Masukkan nama tag"
value={field.value}
onChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{!isDetail && (
<DialogFooter>
<Button
type="submit"
color="primary"
size="md"
className="text-xs"
>
Edit Tag
</Button>
</DialogFooter>
)}
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,181 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link, useRouter } from "@/i18n/routing";
import { NewCampaignIcon } from "@/components/icon";
import {
getCategories,
getListFAQ,
getListFeedback,
getTags,
} from "@/service/settings/settings";
import CreateFAQModal from "./create";
const AdminTagTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [openModal, setOpenModal] = React.useState(false);
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
React.useEffect(() => {
if (dataChange) {
router.push("/admin/settings/tag");
}
fetchData();
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
React.useEffect(() => {
fetchData();
}, [page]);
async function fetchData() {
try {
loading();
const response = await getTags();
const data = response?.data?.data;
data.forEach((item: any, index: number) => {
item.no = (page - 1) * 10 + index + 1;
});
setDataTable(data);
setTotalData(data?.length);
setTotalPage(1);
close();
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">Tag</p>
<CreateFAQModal />
</div>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
{/* <TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/> */}
</div>
);
};
export default AdminTagTable;

View File

@ -1,11 +1,13 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import AdminTagTable from "./component/table";
export default function TagCategory() {
return (
<>
<SiteBreadcrumb />
<AdminTagTable />
</>
);
}

View File

@ -41,6 +41,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
@ -75,24 +77,15 @@ const EscalationTable = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
pageSize: Number(showData),
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
@ -126,19 +119,35 @@ const EscalationTable = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
}, [page, showData]);
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
async function fetchData() {
try {
const res = await getTicketingCollaborationPagination(
page - 1,
limit,
showData,
search
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
@ -148,16 +157,10 @@ const EscalationTable = () => {
console.error("Error fetching tasks:", error);
}
}
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value); // Perbarui state search
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
};
return (
<div className="w-full overflow-x-auto">
<div className="flex justify-between items-center px-5">
<div className="mt-3">
<div className="flex justify-between items-center">
<div className="mt-3 flex flex-row items-center gap-2">
<InputGroup merged>
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
<Search className=" h-4 w-4 dark:text-white" />
@ -167,23 +170,40 @@ const EscalationTable = () => {
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
value={search}
onChange={handleSearch}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="md" variant="outline">
1 - {showData} Data
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56 text-sm">
<DropdownMenuRadioGroup
value={showData}
onValueChange={setShowData}
>
<DropdownMenuRadioItem value="10">
1 - 10 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">
1 - 20 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="25">
1 - 25 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">
1 - 50 Data
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<Table className="overflow-hidden mt-3">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (

View File

@ -12,58 +12,39 @@ import {
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { format } from "date-fns";
import { Link, useRouter } from "@/i18n/routing";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Question",
header: "Pertanyaan",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "commentFromUserName",
header: "CreateBy",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("commentFromUserName")}
</h4>
</div>
</div>
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
),
},
{
accessorKey: "Type",
header: "SendTo",
header: "Channel",
cell: ({ row }) => {
const type = row.original.type; // Akses properti category
return <span className="whitespace-nowrap">{type?.name || "N/A"}</span>;
const type = row.original.type;
return <span className="normal-case">{type?.name || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Upload Date",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
@ -99,6 +80,7 @@ const columns: ColumnDef<any>[] = [
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const router = useRouter();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -111,17 +93,16 @@ const columns: ColumnDef<any>[] = [
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<DropdownMenuItem
onClick={() =>
router.push(
`/shared/communication/collaboration/detail/${row.original.id}`
)
}
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center"
>
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
<Trash2 className="w-4 h-4 me-1.5" />
Delete
Detail
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@ -0,0 +1,143 @@
"use client";
import { Button } from "@/components/ui/button";
import Select, { MultiValue } from "react-select";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useEffect, useState } from "react";
import { Separator } from "@/components/ui/separator";
import {
getCuratorUser,
saveCollaborationTeams,
} from "@/service/communication/communication";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { close, loading } from "@/config/swal";
import { useParams } from "next/navigation";
import { useToast } from "@/components/ui/use-toast";
import { stringify } from "querystring";
const assigneeOptions = [
{ value: "mahedi", label: "Mahedi Amin", image: "/images/avatar/av-1.svg" },
{ value: "sovo", label: "Sovo Haldar", image: "/images/avatar/av-2.svg" },
{
value: "rakibul",
label: "Rakibul Islam",
image: "/images/avatar/av-3.svg",
},
{ value: "pritom", label: "Pritom Miha", image: "/images/avatar/av-4.svg" },
];
export default function UsersCard(props: { team: any; fetchData: () => void }) {
const params = useParams();
const id = params?.id;
const [openSearch, setOpenSearch] = useState(false);
const [selectedUsers, setSelectedUsers] = useState<any>([]);
const [allUser, setAllUser] = useState([]);
const MySwal = withReactContent(Swal);
const { toast } = useToast();
useEffect(() => {
async function getUser() {
const res = await getCuratorUser();
if (res?.data !== null) {
const rawUser = res?.data?.data?.content;
const optionArr: any = [];
rawUser.map((option: any) => {
optionArr.push({
id: option.id,
label: option.username,
value: option.id,
});
});
setAllUser(optionArr);
}
}
getUser();
}, []);
const inviteHandle = () => {
MySwal.fire({
title: "Undang Pengguna Ke Kolom Diskusi?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
inviteUser();
}
});
};
async function inviteUser() {
const userId: any = [];
selectedUsers?.map((user: any) => {
userId.push(user.id);
});
loading();
const res = await saveCollaborationTeams(String(id), userId);
if (res?.error) {
toast({ title: stringify(res?.message) });
return false;
}
close();
toast({ title: "Berhasil menambahkan user" });
props.fetchData();
}
return (
<div className="flex flex-col bg-white p-4 rounded-t-xl w-[30%]">
<div className="flex justify-between items-center mb-3">
<div className="flex flex-row gap-3 items-center">
<p>Kolaborator</p>
<p className="p-2 rounded-full bg-slate-300 w-5 h-5 flex justify-center items-center">
{props?.team?.length}
</p>
</div>
<a onClick={() => setOpenSearch(!openSearch)}>
<Icon icon="material-symbols:search" width={24} />
</a>
</div>
{openSearch && (
<div className="flex flex-col">
<Select
className="react-select my-2 transition-shadow"
classNamePrefix="select"
options={allUser}
isMulti
onChange={(selectedOption) => setSelectedUsers(selectedOption)}
placeholder="Select users"
/>
{selectedUsers?.length > 0 && (
<div className="flex justify-end">
<Button color="primary" size="sm" onClick={inviteHandle}>
Invie
</Button>
</div>
)}
</div>
)}
<div className="mt-3 flex flex-col gap-2">
{props?.team?.map((list: any) => (
<div key={list.id} className="flex flex-col">
<div className="flex flex-row gap-2 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div className="flex flex-col">
<p className="text-sm">{list?.fullname}</p>
<p className="text-xs">{list?.username}</p>
</div>
</div>
<Separator className="my-2" />
</div>
))}
</div>
</div>
);
}

View File

@ -0,0 +1,199 @@
"use client";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Textarea } from "@/components/ui/textarea";
import { useToast } from "@/components/ui/use-toast";
import { close, loading } from "@/config/swal";
import {
deleteCollabDiscussion,
getCollabDiscussion,
getCuratorUser,
getTicketCollaborationTeams,
saveCollabDiscussion,
} from "@/service/communication/communication";
import {
getLocaleTimestamp,
htmlToString,
textEllipsis,
} from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useParams } from "next/navigation";
import { stringify } from "querystring";
import { useEffect, useState } from "react";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import UsersCard from "./component/users-card";
export default function CollaborationPage() {
const params = useParams();
const id = params?.id;
const { toast } = useToast();
const [detailTickets, setDetailTickets] = useState<any>();
const [teams, setTeams] = useState([]);
const [listDiscussion, setListDiscussion] = useState([]);
const [replyValue, setReplyValue] = useState("");
const MySwal = withReactContent(Swal);
useEffect(() => {
async function getCollabTeam() {
if (id != undefined) {
const res = await getTicketCollaborationTeams(String(id));
if (res?.data !== null) {
console.log("response teams:", res);
setDetailTickets(res?.data?.data?.tickets);
setTeams(res?.data?.data?.userTeam);
console.log("userteam", res?.data?.data?.userTeam);
}
}
}
initState();
getCollabTeam();
}, [id]);
async function initState() {
if (id != undefined) {
loading();
const responseGet = await getCollabDiscussion(String(id));
console.log("get res data", responseGet?.data?.data);
close();
setListDiscussion(responseGet?.data?.data);
}
}
async function deleteDataSuggestion(dataId: string | number) {
loading();
const response = await deleteCollabDiscussion(dataId);
console.log(response);
close();
initState();
}
async function sendDiscussionParent() {
if (replyValue?.length > 0) {
loading();
const data = {
ticketId: id,
replyValue,
parentId: null,
};
const response = await saveCollabDiscussion(data);
if (response?.error) {
toast({
title: stringify(response?.message),
});
return false;
}
toast({
title: "Sukses Komen",
});
setReplyValue("");
close();
initState();
}
}
return (
<div>
<SiteBreadcrumb />
<div className="flex flex-row gap-4 pt-6">
<div className="flex flex-col gap-4 w-[70%]">
<div className="bg-white flex flex-col rounded-t-xl">
<div className="flex flex-row gap-2 bg-slate-300 rounded-t-xl p-4 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div className="flex flex-col gap-1">
<div className="flex flex-row">
<p className="text-sm">
<span className="font-semibold">
{detailTickets?.commentFromUserName}
</span>{" "}
mengirimkan komentar untuk
</p>
<a
href={detailTickets?.feed?.permalink_url}
className="font-weight-bold"
>
{textEllipsis(detailTickets?.feed?.message, 25)}
</a>
</div>
<p className="text-xs">
{" "}
{`${new Date(detailTickets?.createdAt).getDate()}-${
new Date(detailTickets?.createdAt).getMonth() + 1
}-${new Date(
detailTickets?.createdAt
).getFullYear()} ${new Date(
detailTickets?.createdAt
).getHours()}:${new Date(
detailTickets?.createdAt
).getMinutes()}`}
</p>
</div>
</div>
<div className="p-4"> {htmlToString(detailTickets?.message)}</div>
</div>
<div className="bg-white rounded-b-xl flex flex-col p-4">
<p className="font-bold text-sm">{listDiscussion?.length} Respon</p>
<Separator className="my-2" />
<div className="flex flex-col gap-2 max-h-[360px]">
{listDiscussion?.length > 1 ? (
listDiscussion?.map((data: any) => (
<div key={data?.id} className="flex flex-col ">
<div className="flex flex-row gap-2 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div className="flex flex-col w-full">
<div className="flex justify-between items-center">
<p className="text-sm font-semibold">
{data?.messageFrom?.fullname}
</p>
<p className="text-xs">
{getLocaleTimestamp(new Date(data?.createdAt))}
</p>
</div>
<div className="flex justify-between items-center">
<p className="text-sm">{data?.message}</p>
<a
onClick={() => deleteDataSuggestion(data?.id)}
className="text-destructive cursor-pointer text-xs"
>
Hapus
</a>
</div>
</div>
</div>
<Separator className="my-2" />
</div>
))
) : (
<p>Belum Ada Tanggapan</p>
)}
</div>
<Textarea
value={replyValue}
onChange={(e) => setReplyValue(e.target.value)}
/>
<div className="flex justify-end py-3">
<Button
className="w-fit"
color="primary"
size="sm"
type="button"
onClick={sendDiscussionParent}
>
Kirim
</Button>
</div>
</div>
</div>{" "}
<UsersCard team={teams} fetchData={() => initState()} />
</div>
</div>
);
}

View File

@ -18,53 +18,33 @@ const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Question",
header: "Pertanyaan",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "commentFromUserName",
header: "CreateBY",
header: "Penerima",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("commentFromUserName")}
</h4>
</div>
</div>
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
),
},
{
accessorKey: "type",
header: "SendTo",
header: "Penerima",
cell: ({ row }) => {
const type = row.original.type; // Akses properti category
return <span className="whitespace-nowrap">{type?.name || "N/A"}</span>;
return <span className="normal-case">{type?.name || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Upload Date",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string

View File

@ -41,6 +41,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
@ -74,24 +76,15 @@ const EscalationTable = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
pageSize: Number(showData),
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
@ -125,19 +118,39 @@ const EscalationTable = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
setPagination({
pageIndex: 0,
pageSize: Number(showData),
});
}, [page, showData]);
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
async function fetchData() {
try {
const res = await getTicketingEscalationPagination(
page - 1,
limit,
Number(showData),
search
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
@ -155,8 +168,8 @@ const EscalationTable = () => {
return (
<div className="w-full overflow-x-auto">
<div className="flex justify-between items-center px-5">
<div className="mt-3">
<div className="flex justify-between items-center">
<div className="mt-3 flex flex-row items-center gap-2">
<InputGroup merged>
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
<Search className=" h-4 w-4 dark:text-white" />
@ -166,22 +179,38 @@ const EscalationTable = () => {
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
value={search}
onChange={handleSearch}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="md" variant="outline">
1 - {showData} Data
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56 text-sm">
<DropdownMenuRadioGroup
value={showData}
onValueChange={setShowData}
>
<DropdownMenuRadioItem value="10">
1 - 10 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">
1 - 20 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="25">
1 - 25 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">
1 - 50 Data
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<Table className="overflow-hidden mt-3">
<TableHeader>

View File

@ -18,54 +18,36 @@ const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
cell: ({ row }) => <span> {row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Question",
header: "Pertanyaan",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
<span className="normal-case"> {row.getValue("title")}</span>
),
},
{
accessorKey: "createdBy",
header: "CreateBy",
header: "Pengirim",
cell: ({ row }) => {
const createdBy = row.original.createdBy; // Akses properti category
return (
<span className="whitespace-nowrap">
{createdBy?.fullname || "N/A"}
</span>
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
);
},
},
{
accessorKey: "sendTo",
header: "SendTo",
header: "Penerima",
cell: ({ row }) => {
const sendTo = row.original.sendTo; // Akses properti category
return (
<span className="whitespace-nowrap">{sendTo?.fullname || "N/A"}</span>
);
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
},
},
{
accessorKey: "createdAt",
header: "Upload Date",
header: "Waktu",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string

View File

@ -42,6 +42,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
@ -73,25 +75,18 @@ const TableAudio = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
pageSize: Number(showData),
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const table = useReactTable({
@ -124,15 +119,39 @@ const TableAudio = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, search]);
setPagination({
pageIndex: 0,
pageSize: Number(showData),
});
}, [page, showData]);
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
async function fetchData() {
try {
const res = await listTicketingInternal(page - 1, limit, search);
const res = await listTicketingInternal(
page - 1,
Number(showData),
search
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
@ -145,21 +164,10 @@ const TableAudio = () => {
}
}
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value); // Perbarui state search
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
};
return (
<div className="w-full overflow-x-auto ">
<div className="flex justify-between items-center px-5">
<div className="flex justify-between items-center">
<div className="mt-3 flex flex-row items-center gap-2">
<Link href={"/shared/communication/internal/create"}>
<Button color="primary" className="text-white" size="md">
<UploadIcon />
Pertanyaan Baru
</Button>
</Link>
<InputGroup merged>
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
<Search className=" h-4 w-4 dark:text-white" />
@ -169,22 +177,38 @@ const TableAudio = () => {
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
value={search}
onChange={handleSearch}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="md" variant="outline">
1 - {showData} Data
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56 text-sm">
<DropdownMenuRadioGroup
value={showData}
onValueChange={setShowData}
>
<DropdownMenuRadioItem value="10">
1 - 10 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">
1 - 20 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="25">
1 - 25 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">
1 - 50 Data
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<Table className="overflow-hidden mt-3">
<TableHeader>

View File

@ -1,3 +1,4 @@
"use client";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import InternalTable from "./internal/components/internal-table";
@ -5,71 +6,77 @@ import SiteBreadcrumb from "@/components/site-breadcrumb";
import CollaborationTable from "./collaboration/components/collabroation-table";
import EscalationTable from "./escalation/components/escalation-table";
import { useState } from "react";
import { Link, useRouter } from "@/i18n/routing";
import { PlusIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
const CommunicationPage = () => {
const [tab, setTab] = useState("Komunikasi");
return (
<div>
<SiteBreadcrumb />
<div className="my-3">
<Card className="py-3 px-2 my-4">
<Tabs defaultValue="internal" className="w-full">
<p className="text-lg font-semibold ml-2">Komunikasi</p>
<TabsList className="flex-wrap">
<TabsTrigger
value="internal"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
Pertanyaan Internal
</TabsTrigger>
<TabsTrigger
value="escalation"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
Eskalasi
</TabsTrigger>
<TabsTrigger
value="collaboration"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
Kolaborasi
</TabsTrigger>
</TabsList>
<TabsContent value="internal">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card>
<CardContent className="p-0">
<InternalTable />
</CardContent>
</Card>
</div>
</div>
</TabsContent>
<TabsContent value="escalation">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card>
<CardContent className="p-0">
<EscalationTable />
</CardContent>
</Card>
</div>
</div>
</TabsContent>
<TabsContent value="collaboration">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card>
<CardContent className="p-0">
<CollaborationTable />
</CardContent>
</Card>
</div>
</div>
</TabsContent>
</Tabs>
</Card>
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between py-3">
<p className="text-lg">{tab}</p>
{tab === "Komunikasi" && (
<Link href="/shared/communication/internal/create">
<Button color="primary" size="md">
<PlusIcon />
Pertanyaan baru
</Button>
</Link>
)}
{tab === "Kolaborasi" && (
<Link href="/shared/communication/collaboration/create">
<Button color="primary" size="md">
<PlusIcon />
Kolaborasi baru
</Button>
</Link>
)}
</div>
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button
rounded="md"
onClick={() => setTab("Komunikasi")}
className={` hover:text-white
${
tab === "Komunikasi"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Komunikasi
</Button>
<Button
rounded="md"
onClick={() => setTab("Eskalasi")}
className={` hover:text-white
${
tab === "Eskalasi"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Eskalasi
</Button>
<Button
rounded="md"
onClick={() => setTab("Kolaborasi")}
className={` hover:text-white
${
tab === "Kolaborasi"
? "bg-black text-white "
: "bg-white text-black "
}`}
>
Kolaborasi
</Button>
</div>
{tab === "Komunikasi" && <InternalTable />}
{tab === "Eskalasi" && <EscalationTable />}
{tab === "Kolaborasi" && <CollaborationTable />}
</div>
</div>
);

View File

@ -0,0 +1,172 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { formatDateToIndonesian } from "@/utils/globals";
import { Link, useRouter } from "@/i18n/routing";
import { setBanner } from "@/service/settings/settings";
import { error } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { deleteUser } from "@/service/management-user/management-user";
import { stringify } from "querystring";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "fullname",
header: "Nama",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("fullname")}</span>
),
},
{
accessorKey: "username",
header: "Username",
cell: ({ row }) => (
<span className="normal-case">
{row.original?.userKeycloak?.username || ""}
</span>
),
},
{
accessorKey: "phoneNumber",
header: "No. HP",
cell: ({ row }) => <span>{row.getValue("phoneNumber")}</span>,
},
{
accessorKey: "email",
header: "Email",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("email")}</span>
),
},
{
accessorKey: "level",
header: "Level Pengguna",
cell: ({ row }) => (
<span className="normal-case">{row.original?.role?.name || ""}</span>
),
},
{
accessorKey: "createdAt",
header: "Tanggal Unggah",
cell: ({ row }) => (
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
),
},
{
accessorKey: "isActive",
header: "Status",
cell: ({ row }) => (
<span
className={
row.getValue("isActive") ? "text-success" : "text-destructive"
}
>
{row.getValue("isActive") ? "Aktif" : "Belum Aktif"}
</span>
),
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const { toast } = useToast();
const MySwal = withReactContent(Swal);
const router = useRouter();
const doDelete = async (id: number) => {
const response = await deleteUser(id);
if (response?.error) {
toast({
title: stringify(response?.message),
variant: "destructive",
});
}
toast({
title: "Success delete",
});
router.push("?dataChange=true");
};
const handleDelete = (id: number) => {
MySwal.fire({
title: "Apakah anda ingin menghapus data user?",
showCancelButton: true,
confirmButtonColor: "#dc3545",
confirmButtonText: "Iya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<a>Aktivasi</a>
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Link
href={`/admin/management-user/external/detail/${row.original.id}`}
>
Detail
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Link
href={`/admin/management-user/external/edit/${row.original.id}`}
>
Edit
</Link>
</DropdownMenuItem>
<DropdownMenuItem
color="red"
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
>
<a
onClick={() => {
handleDelete(row.original.id);
}}
>
Hapus
</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -0,0 +1,390 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { PlusIcon } from "lucide-react";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./management-user-external-column-table";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { useRouter, Link } from "@/i18n/routing";
import { AdministrationUserList } from "@/service/management-user/management-user";
const UserExternalTable = () => {
const router = useRouter();
const levelId = getCookiesDecrypt("ulie");
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
});
const [search, setSearch] = React.useState("");
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
React.useEffect(() => {
router.push("/admin/management-user");
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
React.useEffect(() => {
fetchData();
setPagination({
pageIndex: 0,
pageSize: Number(showData),
});
}, [page, showData]);
async function fetchData() {
try {
loading();
const res = await AdministrationUserList(
String(levelId),
page - 1,
search,
showData,
"2",
statusFilter?.sort().join(",")
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
close();
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
const handleChange = (type: string, id: number, checked: boolean) => {
if (type === "category") {
if (checked) {
const temp: number[] = [...categoryFilter];
temp.push(id);
setCategoryFilter(temp);
} else {
const temp = categoryFilter.filter((a) => a !== id);
setCategoryFilter(temp);
}
} else {
if (checked) {
const temp: number[] = [...statusFilter];
temp.push(id);
setStatusFilter(temp);
} else {
const temp = statusFilter.filter((a) => a !== id);
setStatusFilter(temp);
}
}
};
return (
<>
<div className="flex justify-between py-3">
<Input
type="text"
placeholder="Search"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onChange={(e) => setSearch(e.target.value)}
className="max-w-[300px]"
/>
<div className="flex flex-row gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="md" variant="outline">
1 - {showData} Data
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56 text-sm">
<DropdownMenuRadioGroup
value={showData}
onValueChange={setShowData}
>
<DropdownMenuRadioItem value="10">
1 - 10 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">
1 - 20 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="25">
1 - 25 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">
1 - 50 Data
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
<Popover>
<PopoverTrigger asChild>
<Button size="md" variant="outline">
Filter
</Button>
</PopoverTrigger>
<PopoverContent className="w-80 ">
<div className="flex flex-col gap-2 px-2">
<div className="flex justify-between text-sm">
<p>Filter</p>
<a
onClick={() => fetchData()}
className="cursor-pointer text-primary"
>
Simpan
</a>
</div>
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
<p>Admin</p>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(3)}
onCheckedChange={(e) =>
handleChange("status", 3, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Approver
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(4)}
onCheckedChange={(e) =>
handleChange("status", 4, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Kontributor
</label>
</div>
<p className="mt-3">Kurator</p>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(11)}
onCheckedChange={(e) =>
handleChange("status", 11, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Koordinator
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(12)}
onCheckedChange={(e) =>
handleChange("status", 12, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Kurator
</label>
</div>
<p className="mt-3">Feedback Center</p>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(9)}
onCheckedChange={(e) =>
handleChange("status", 9, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Supervisor
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(10)}
onCheckedChange={(e) =>
handleChange("status", 10, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Operator
</label>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
</div>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</>
);
};
export default UserExternalTable;

View File

@ -0,0 +1,166 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { formatDateToIndonesian } from "@/utils/globals";
import { Link, useRouter } from "@/i18n/routing";
import { useToast } from "@/components/ui/use-toast";
import { deleteUser } from "@/service/management-user/management-user";
import { stringify } from "querystring";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "fullname",
header: "Nama",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("fullname")}</span>
),
},
{
accessorKey: "username",
header: "Username",
cell: ({ row }) => (
<span className="normal-case">
{row.original?.userKeycloak?.username || ""}
</span>
),
},
{
accessorKey: "phoneNumber",
header: "No. HP",
cell: ({ row }) => <span>{row.getValue("phoneNumber")}</span>,
},
{
accessorKey: "email",
header: "Email",
cell: ({ row }) => (
<span className="normal-case">{row.getValue("email")}</span>
),
},
{
accessorKey: "level",
header: "Level Pengguna",
cell: ({ row }) => (
<span className="normal-case">{row.original.userLevel.name}</span>
),
},
{
accessorKey: "createdAt",
header: "Tanggal Unggah",
cell: ({ row }) => (
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
),
},
{
accessorKey: "isActive",
header: "Status",
cell: ({ row }) => (
<span
className={
row.getValue("isActive") ? "text-success" : "text-destructive"
}
>
{row.getValue("isActive") ? "Aktif" : "Belum Aktif"}
</span>
),
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
const { toast } = useToast();
const MySwal = withReactContent(Swal);
const router = useRouter();
const doDelete = async (id: number) => {
const response = await deleteUser(id);
if (response?.error) {
toast({
title: stringify(response?.message),
variant: "destructive",
});
}
toast({
title: "Success delete",
});
router.push("?dataChange=true");
};
const handleDelete = (id: number) => {
MySwal.fire({
title: "Apakah anda ingin menghapus data user?",
showCancelButton: true,
confirmButtonColor: "#dc3545",
confirmButtonText: "Iya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Link
href={`/admin/management-user/internal/detail/${row.original.id}`}
>
Detail
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Link
href={`/admin/management-user/internal/edit/${row.original.id}`}
>
Edit
</Link>
</DropdownMenuItem>
<DropdownMenuItem
color="red"
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
>
<a
onClick={() => {
handleDelete(row.original.id);
}}
>
Hapus
</a>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];
export default columns;

View File

@ -0,0 +1,410 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
PlusIcon,
Search,
SquarePen,
Trash2,
TrendingDown,
TrendingUp,
UserIcon,
} from "lucide-react";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import { paginationBlog } from "@/service/blog/blog";
import { ticketingPagination } from "@/service/ticketing/ticketing";
import { Badge } from "@/components/ui/badge";
import { useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./management-user-internal-column-table";
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { listDataMedia } from "@/service/broadcast/broadcast";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link, useRouter } from "@/i18n/routing";
import { AdministrationUserList } from "@/service/management-user/management-user";
const UserInternalTable = () => {
const router = useRouter();
const levelId = getCookiesDecrypt("ulie");
const searchParams = useSearchParams();
const dataChange = searchParams?.get("dataChange");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
});
const [search, setSearch] = React.useState("");
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
fetchData();
}
React.useEffect(() => {
fetchData();
router.push("/admin/management-user");
}, [dataChange]);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
React.useEffect(() => {
fetchData();
setPagination({
pageIndex: 0,
pageSize: Number(showData),
});
}, [page, showData]);
async function fetchData() {
try {
loading();
const res = await AdministrationUserList(
String(levelId),
page - 1,
search,
showData,
"1",
statusFilter?.sort().join(",")
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * Number(showData) + index + 1;
});
console.log("contentData : ", contentData);
setDataTable(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
close();
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
const handleChange = (type: string, id: number, checked: boolean) => {
if (type === "category") {
if (checked) {
const temp: number[] = [...categoryFilter];
temp.push(id);
setCategoryFilter(temp);
} else {
const temp = categoryFilter.filter((a) => a !== id);
setCategoryFilter(temp);
}
} else {
if (checked) {
const temp: number[] = [...statusFilter];
temp.push(id);
setStatusFilter(temp);
} else {
const temp = statusFilter.filter((a) => a !== id);
setStatusFilter(temp);
}
}
};
return (
<>
<div className="flex justify-between py-3">
<Input
type="text"
placeholder="Search"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onChange={(e) => setSearch(e.target.value)}
className="max-w-[300px]"
/>
<div className="flex flex-row gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="md" variant="outline">
1 - {showData} Data
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56 text-sm">
<DropdownMenuRadioGroup
value={showData}
onValueChange={setShowData}
>
<DropdownMenuRadioItem value="10">
1 - 10 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">
1 - 20 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="25">
1 - 25 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">
1 - 50 Data
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
<Popover>
<PopoverTrigger asChild>
<Button size="md" variant="outline">
Filter
</Button>
</PopoverTrigger>
<PopoverContent className="w-80 ">
<div className="flex flex-col gap-2 px-2">
<div className="flex justify-between text-sm">
<p>Filter</p>
<a
onClick={() => fetchData()}
className="cursor-pointer text-primary"
>
Simpan
</a>
</div>
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
<p>Admin</p>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(3)}
onCheckedChange={(e) =>
handleChange("status", 3, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Approver
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(4)}
onCheckedChange={(e) =>
handleChange("status", 4, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Kontributor
</label>
</div>
<p className="mt-3">Kurator</p>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(11)}
onCheckedChange={(e) =>
handleChange("status", 11, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Koordinator
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(12)}
onCheckedChange={(e) =>
handleChange("status", 12, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Kurator
</label>
</div>
<p className="mt-3">Feedback Center</p>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(9)}
onCheckedChange={(e) =>
handleChange("status", 9, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Supervisor
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="accepted"
checked={statusFilter.includes(10)}
onCheckedChange={(e) =>
handleChange("status", 10, Boolean(e))
}
/>
<label
htmlFor="accepted"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Operator
</label>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
</div>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</>
);
};
export default UserInternalTable;

View File

@ -0,0 +1,80 @@
"use client";
import Cookies from "js-cookie";
import { useEffect, useState } from "react";
import { getCookiesDecrypt } from "@/lib/utils";
import { generateTicket } from "@/service/tableau/tableau-service";
import { Button } from "../ui/button";
import { useTranslations } from "next-intl";
export default function ManagementUserVisualization() {
const [ticket, setTicket] = useState("");
const baseUrl = "https://db-mediahub.polri.go.id/";
const url = "https://db-mediahub.polri.go.id/trusted/";
const view = "views/2022_05_MediaHUB-Viz_Rev110/db-user-count?:iid=5&";
const param = ":embed=yes&:toolbar=yes&:iframeSizedToWindow=true";
const [isInternational, setIsInternational] = useState(false);
const t = useTranslations("AnalyticsDashboard");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
const initState = async () => {
const response = await generateTicket();
console.log("Data :", response?.data?.data);
setTicket(response?.data?.data);
console.log(userId);
};
initState();
}, []);
return (
<div className="flex flex-col gap-2 bg-white rounded-lg p-3">
{isInternational ? (
<p className="font-semibold">STATISTICS TO THE NUMBER OF USERS</p>
) : (
<p className="font-semibold">STATISTIK JUMLAH PENGGUNA</p>
)}
<div className="flex flex-col gap-1">
<p>{t("choose_category")}</p>
<div className="flex flex-row gap-1 border-2 w-fit">
<Button
onClick={() => setIsInternational(false)}
className={` hover:text-white rounded-none
${
isInternational
? "bg-white text-black "
: "bg-black text-white "
}`}
>
Indonesia
</Button>
<Button
onClick={() => setIsInternational(true)}
className={`hover:text-white rounded-none ${
isInternational ? "bg-black text-white " : "bg-white text-black "
}
`}
>
{t("international")}
</Button>
</div>
</div>
{ticket == undefined ? (
<iframe
src={`${baseUrl + view + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket}/${view}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
);
}

View File

@ -8,18 +8,10 @@ import {
} from "./http-config/http-interceptor-service";
export async function login(data: any) {
const res = await getCsrfToken();
const csrfToken = res?.data?.token;
console.log("Token CSRF : ", csrfToken);
const pathUrl = "signin";
const headers = {
'accept': 'application/json',
'content-type': 'application/json',
'X-XSRF-TOKEN': csrfToken
};
return httpPost(pathUrl, headers, data);
}

View File

@ -1,4 +1,5 @@
import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
@ -71,3 +72,31 @@ export async function getTicketingDetail(id: any) {
const url = `ticketing?id=${id}`;
return httpGetInterceptor(url);
}
export async function getTicketCollaborationTeams(id: string | number) {
const url = `ticketing/collaboration/teams?ticketId=${id}`;
return httpGetInterceptor(url);
}
export async function saveCollaborationTeams(
id: string | number,
userId: string | number
) {
const url = `ticketing/collaboration/teams?ticketId=${id}&userId=${userId}`;
return httpPostInterceptor(url);
}
export async function getCollabDiscussion(id: string | number) {
const url = `ticketing/collaboration/discussion?ticketId=${id}`;
return httpGetInterceptor(url);
}
export async function saveCollabDiscussion(data: any) {
const url = "ticketing/collaboration/discussion";
return httpPostInterceptor(url, data);
}
export async function deleteCollabDiscussion(id: string | number) {
const url = `ticketing/collaboration/discussion?id=${id}`;
return httpDeleteInterceptor(url);
}

View File

@ -2,10 +2,24 @@ import axiosBaseInstance from "./axios-base-instance";
import axios from "axios";
import Cookies from "js-cookie";
import qs from "qs";
import { getCsrfToken } from "../auth";
export async function httpPost(pathUrl: any, headers: any, data?: any) {
const response = await axiosBaseInstance
.post(pathUrl, data, { headers })
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
const defaultHeaders = {
"Content-Type": "application/json",
};
const mergedHeaders = {
...defaultHeaders,
...headers,
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
};
const response = await axiosBaseInstance
.post(pathUrl, data, { headers: mergedHeaders })
.catch(function (error: any) {
console.log(error);
return error.response;
@ -50,8 +64,21 @@ export async function httpGet(pathUrl: any, headers: any) {
}
export async function httpPut(pathUrl: any, headers: any, data?: any) {
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
const defaultHeaders = {
"Content-Type": "application/json",
};
const mergedHeaders = {
...defaultHeaders,
...headers,
...(csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {}),
};
const response = await axiosBaseInstance
.put(pathUrl, data, { headers })
.put(pathUrl, data, { headers: mergedHeaders })
.catch(function (error: any) {
console.log(error);
return error.response;

View File

@ -0,0 +1,56 @@
import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
export async function AdministrationUserList(
id: string,
page: number,
name = "",
size: string,
featureId: string,
role = ""
) {
const url = `users/pagination/internal?enablePage=1&size=${size}&page=${page}&levelId=${id}&name=${name}&featureId=${featureId}&roleFilter=${role}`;
return httpGetInterceptor(url);
}
export async function AdministrationLevelList() {
const url = "users/user-levels/list";
return httpGetInterceptor(url);
}
export async function getListEducation() {
const url = "users/user-educations/list";
return httpGetInterceptor(url);
}
export async function getListSchools() {
const url = "users/user-schools/list";
return httpGetInterceptor(url);
}
export async function getListCompetencies() {
const url = "users/user-competencies/list";
return httpGetInterceptor(url);
}
export async function saveUserInternal(data: any) {
const url = "users/save";
return httpPostInterceptor(url, data);
}
export async function getUserById(id: string) {
const url = `users?id=${id}`;
return httpGetInterceptor(url);
}
export async function disableUser(userId: number | string) {
const url = `users/disable-user?userId=${userId}`;
return httpPostInterceptor(url);
}
export async function deleteUser(userId: number | string) {
const url = `users/delete-user?userId=${userId}`;
return httpDeleteInterceptor(url);
}

View File

@ -9,6 +9,11 @@ export async function getCategories(page: number) {
return httpGetInterceptor(url);
}
export async function getCategoriesAll() {
const url = "media/categories/list?enablePage=0&size=100&sort=desc&sortBy=id";
return httpGetInterceptor(url);
}
export async function publishUnpublishCategory(id: number, status: string) {
const url = `media/categories/publish?id=${id}&status=${status}`;
return httpPostInterceptor(url);
@ -66,3 +71,61 @@ export async function setBanner(id: number, status: boolean) {
const url = `media/banner?id=${id}&status=${status}`;
return httpPostInterceptor(url);
}
export async function getListFAQ() {
const url = "master/faq/list";
return httpGetInterceptor(url);
}
export async function postDataFAQ(data: any) {
const url = "master/faq";
return httpPostInterceptor(url, data);
}
export async function deleteDataFAQ(id: string) {
const url = `master/faq/${id}`;
return httpDeleteInterceptor(url);
}
export async function detailDataFAQ(id: string) {
const url = `master/faq/${id}`;
return httpGetInterceptor(url);
}
export async function getListFeedback() {
const url = "master/feedback/list";
return httpGetInterceptor(url);
}
// export async function getUserFeedbacks() {
// const url = 'feedback/list-all';
// return httpGetInterceptor({ url });
// }
export async function detailDataFeedback(id: string) {
const url = `master/feedback/${id}`;
return httpGetInterceptor(url);
}
export async function postDataFeedback(data: any) {
const url = "master/feedback";
return httpPostInterceptor(url, data);
}
export async function deleteDataFeedback(id: string | number) {
const url = `master/feedback/${id}`;
return httpDeleteInterceptor(url);
}
export async function getTags() {
const url = "media/tags/list";
return httpGetInterceptor(url);
}
export async function getTagsBySubCategoryId(subCategory: string | number) {
const url = `media/tags/list?subCategoryId=${subCategory}`;
return httpGetInterceptor(url);
}
export async function getTagsByParentId(parentId: string | number) {
const url = `media/tags/list?categoryId=${parentId}`;
return httpGetInterceptor(url);
}

View File

@ -5,7 +5,7 @@
/**
* @module paste-from-office
*/
export { default as PasteFromOffice } from './pastefromoffice.js';
export { default as MSWordNormalizer } from './normalizers/mswordnormalizer.js';
export { parseHtml } from './filters/parse.js';
import './augmentation.js';
export { default as PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js';
export { default as MSWordNormalizer } from '@ckeditor/ckeditor5-paste-from-office/src/normalizers/mswordnormalizer.js';
export { parseHtml } from '@ckeditor/ckeditor5-paste-from-office/src/filters/parse.js';
import '@ckeditor/ckeditor5-paste-from-office/src/augmentation.js';

View File

@ -1,4 +0,0 @@
Changelog
=========
All changes in the package are documented in the main repository. See: https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md.

View File

@ -1,17 +0,0 @@
Software License Agreement
==========================
**CKEditor&nbsp;5 source editing feature** https://github.com/ckeditor/ckeditor5-source-editing <br>
Copyright (c) 20032024, [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.

View File

@ -1,20 +0,0 @@
CKEditor&nbsp;5 source editing feature
=================================
[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-source-editing.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-source-editing)
[![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
[![Build Status](https://travis-ci.com/ckeditor/ckeditor5.svg?branch=master)](https://app.travis-ci.com/github/ckeditor/ckeditor5)
This package implements the source editing support for CKEditor&nbsp;5 that allows you to view and edit the source of the document.
## Demo
Check out the [demo in the source editing feature guide](https://ckeditor.com/docs/ckeditor5/latest/features/source-editing.html#demo).
## Documentation
See the [`@ckeditor/ckeditor5-source-editing` package](https://ckeditor.com/docs/ckeditor5/latest/api/source-editing.html) page as well as the [source editing feature](https://ckeditor.com/docs/ckeditor5/latest/features/source-editing.html) guide in the [CKEditor&nbsp;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).

View File

@ -1 +0,0 @@
!function(o){const n=o.hu=o.hu||{};n.dictionary=Object.assign(n.dictionary||{},{"Show source":"Forrás megjelenítése",Source:"Forrás"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(i){const n=i.id=i.id||{};n.dictionary=Object.assign(n.dictionary||{},{"Show source":"Tampilkan sumber",Source:"Sumber"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.it=o.it||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Mostra sorgente",Source:"Sorgente"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.ja=o.ja||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"ソースを表示",Source:"ソース"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.ko=o.ko||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"소스 표시",Source:"소스"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(i){const n=i.lt=i.lt||{};n.dictionary=Object.assign(n.dictionary||{},{"Show source":"Rodyti šaltinį",Source:"Šaltinis"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.lv=o.lv||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Rādīt avotu",Source:"Pirmavots"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(n){const o=n.ms=n.ms||{};o.dictionary=Object.assign(o.dictionary||{},{"Show source":"Paparkan sumber",Source:"Sumber"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(n){const o=n.nl=n.nl||{};o.dictionary=Object.assign(o.dictionary||{},{"Show source":"Bron tonen",Source:"Bron"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(i){const o=i.no=i.no||{};o.dictionary=Object.assign(o.dictionary||{},{"Show source":"Vis kilde",Source:"Kilde"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.pl=o.pl||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Pokaż źródło",Source:"Źródło"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o["pt-br"]=o["pt-br"]||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Exibir fonte",Source:"Código-Fonte"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const n=o.pt=o.pt||{};n.dictionary=Object.assign(n.dictionary||{},{"Show source":"Mostrar fonte",Source:"Fonte"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.ro=o.ro||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Afișare sursă",Source:"Sursă"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.ru=o.ru||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Показать источник",Source:"HTML редактор"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.sk=o.sk||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Zobraziť zdroj",Source:"Zdroj"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(n){const o=n["sr-latn"]=n["sr-latn"]||{};o.dictionary=Object.assign(o.dictionary||{},{"Show source":"",Source:"Izvor"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.sr=o.sr||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Pokaži izvor",Source:"Извор"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(i){const o=i.sv=i.sv||{};o.dictionary=Object.assign(o.dictionary||{},{"Show source":"Visa källa",Source:"Källa"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.th=o.th||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"แสดงที่มา",Source:"ซอร์ส"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(n){const o=n.tr=n.tr||{};o.dictionary=Object.assign(o.dictionary||{},{"Show source":"Kaynağı göster",Source:"Kaynak"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.ug=o.ug||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"",Source:"مەنبە"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.uk=o.uk||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Показати джерело",Source:"Джерело"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.ur=o.ur||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"",Source:"مآخذ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(n){const i=n.vi=n.vi||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"Hiển thị nguồn",Source:"Nguồn"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(n){const c=n["zh-cn"]=n["zh-cn"]||{};c.dictionary=Object.assign(c.dictionary||{},{"Show source":"显示源代码",Source:"源代码"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1 +0,0 @@
!function(o){const i=o.zh=o.zh||{};i.dictionary=Object.assign(i.dictionary||{},{"Show source":"顯示來源",Source:"原始碼"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));

View File

@ -1,18 +0,0 @@
{
"plugins": [
{
"name": "Source editing",
"className": "SourceEditing",
"description": "Allows for viewing and editing the source of the document.",
"docs": "features/source-editing.html",
"path": "src/sourceediting.js",
"uiComponents": [
{
"type": "Button",
"name": "sourceEditing",
"iconPath": "theme/icons/source-editing.svg"
}
]
}
]
}

View File

@ -1,4 +0,0 @@
{
"Source": "The label of the source editing feature toolbar button.",
"Show source": "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
}

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "المصدر"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "إظهار المصدر"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Източник"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Показване на източника"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "উৎস"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "উৎস দেখান"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Font"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Mostrar la font"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Zdroj"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Zobrazit zdroj"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Kilde"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Vis kilde"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Quellcode"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Quelle anzeigen"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Κώδικας"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Εμφάνιση πηγής"

View File

@ -1,26 +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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Source"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr ""

View File

@ -1,26 +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: \n"
"Language-Team: \n"
"Plural-Forms: \n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Source"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Show source"

View File

@ -1,26 +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: Spanish (https://app.transifex.com/ckeditor/teams/11143/es/)\n"
"Language: es\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Origen"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Mostrar fuente"

View File

@ -1,26 +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: Estonian (https://app.transifex.com/ckeditor/teams/11143/et/)\n"
"Language: et\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Allikas"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Näita allikat"

View File

@ -1,26 +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: Finnish (https://app.transifex.com/ckeditor/teams/11143/fi/)\n"
"Language: fi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Lähde"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Näytä lähde"

View File

@ -1,26 +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: French (https://app.transifex.com/ckeditor/teams/11143/fr/)\n"
"Language: fr\n"
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Source"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Afficher la source"

View File

@ -1,26 +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: Galician (https://app.transifex.com/ckeditor/teams/11143/gl/)\n"
"Language: gl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Orixe"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr ""

View File

@ -1,26 +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: Hebrew (https://app.transifex.com/ckeditor/teams/11143/he/)\n"
"Language: he\n"
"Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "מקור"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "הצג מקור"

View File

@ -1,26 +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: Hindi (https://app.transifex.com/ckeditor/teams/11143/hi/)\n"
"Language: hi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "सोर्स"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "सोर्स दिखाएं"

View File

@ -1,26 +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: Croatian (https://app.transifex.com/ckeditor/teams/11143/hr/)\n"
"Language: hr\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 "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Izvorni kod"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr ""

View File

@ -1,26 +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: Hungarian (https://app.transifex.com/ckeditor/teams/11143/hu/)\n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Forrás"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Forrás megjelenítése"

View File

@ -1,26 +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: Indonesian (https://app.transifex.com/ckeditor/teams/11143/id/)\n"
"Language: id\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Sumber"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Tampilkan sumber"

View File

@ -1,26 +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: Italian (https://app.transifex.com/ckeditor/teams/11143/it/)\n"
"Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"Content-Type: text/plain; charset=UTF-8\n"
msgctxt "The label of the source editing feature toolbar button."
msgid "Source"
msgstr "Sorgente"
msgctxt "The accessible label of the menu bar button that changes the editor mode to raw source (HTML) editing."
msgid "Show source"
msgstr "Mostra sorgente"

Some files were not shown because too many files have changed in this diff Show More