fix: fixing bugs report friday

This commit is contained in:
Sabda Yagra 2026-02-02 11:31:13 +07:00
parent 5b6ec0342b
commit 2b545ec51a
15 changed files with 279 additions and 150 deletions

View File

@ -71,7 +71,7 @@ const TableCategories = () => {
{
accessorKey: "statusId",
header: "Status",
cell: ({ row }) => (row.original.isPublish ? "Publish" : "Draft"),
cell: ({ row }) => (row.original.isActive ? "Active" : "Draft"),
},
{
id: "actions",

View File

@ -6,12 +6,12 @@ import TableImage from "./table-image";
import PendingApprovalTable from "./pending-approval-table";
const ImageTabs = () => {
const [activeTab, setActiveTab] = React.useState("pending");
const [activeTab, setActiveTab] = React.useState("submitted");
return (
<div className="w-full">
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<div className="flex items-center justify-between mb-6">
{/* <div className="flex items-center justify-between mb-6">
<TabsList className="inline-flex h-10 bg-gray-50 border border-gray-200 p-1 rounded-lg shadow-sm">
<TabsTrigger
value="pending"
@ -33,18 +33,18 @@ const ImageTabs = () => {
</TabsTrigger>
</TabsList>
</div>
*/}
<TabsContent value="submitted" className="mt-0">
<div className="bg-white rounded-lg border border-gray-200 shadow-sm">
<TableImage />
</div>
</TabsContent>
<TabsContent value="pending" className="mt-0">
{/* <TabsContent value="pending" className="mt-0">
<div className="bg-white rounded-lg border border-gray-200 shadow-sm">
<PendingApprovalTable typeId={1} />
</div>
</TabsContent>
</TabsContent> */}
</Tabs>
</div>
);

View File

@ -290,9 +290,9 @@ const TableImage = () => {
return (
<div className="w-full overflow-x-auto">
<div className="flex flex-col md:flex-row lg:flex-row md:justify-between lg:justify-between items-center md:px-5 lg:px-5">
<div className="relative w-full md:w-[200px] lg:w-[200px] px-2">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 dark:text-white" />
<Input
<div className="relative w-full md:w-[200px] lg:w-[200px] px-2 border border-slate-200">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 " />
<input
type="text"
placeholder="Search Judul..."
className="pl-9 bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"

View File

@ -5,7 +5,7 @@ const ImageCreatePage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4 bg-slate-100">
<div className="space-y-4 bg-slate-100 dark:bg-default-50">
<FormImage />
</div>
</div>

View File

@ -11,7 +11,7 @@ const AudioVisualTabs = () => {
return (
<div className="w-full">
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<div className="flex items-center justify-between mb-6">
{/* <div className="flex items-center justify-between mb-6">
<TabsList className="inline-flex h-10 bg-gray-50 border border-gray-200 p-1 rounded-lg shadow-sm">
<TabsTrigger
value="submitted"
@ -32,7 +32,7 @@ const AudioVisualTabs = () => {
</div>
</TabsTrigger>
</TabsList>
</div>
</div> */}
<TabsContent value="submitted" className="mt-0">
<div className="bg-white rounded-lg border border-gray-200 shadow-sm">
@ -40,11 +40,11 @@ const AudioVisualTabs = () => {
</div>
</TabsContent>
<TabsContent value="pending" className="mt-0">
{/* <TabsContent value="pending" className="mt-0">
<div className="bg-white rounded-lg border border-gray-200 shadow-sm">
<PendingApprovalTable typeId={2} />
</div>
</TabsContent>
</TabsContent> */}
</Tabs>
</div>
);

View File

@ -21,7 +21,7 @@ export default function AdminPage() {
return (
<motion.div
className="h-full overflow-auto bg-gray-50 dark:bg-black"
className="h-full overflow-auto"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}

View File

@ -204,7 +204,7 @@ function TenantSettingsContentTable() {
Manage approval workflows and user levels for your tenant
</p>
</div>
<div className="flex items-center gap-2">
{/* <div className="flex items-center gap-2">
<SettingsIcon className="h-6 w-6 text-gray-500" />
<Button variant="outline" size="sm" onClick={checkWorkflowStatus}>
Check Workflow Status
@ -217,7 +217,7 @@ function TenantSettingsContentTable() {
>
Test Modal
</Button>
</div>
</div> */}
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
@ -266,7 +266,8 @@ function TenantSettingsContentTable() {
)}
</div>
{isEditingWorkflow && workflow && workflow.workflow?.id ? (
{/* {isEditingWorkflow && workflow && workflow.workflow?.id ? ( */}
{isEditingWorkflow ? (
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
@ -281,8 +282,8 @@ function TenantSettingsContentTable() {
</CardHeader>
<CardContent>
<ApprovalWorkflowForm
key={workflow.workflow.id}
workflowId={workflow.workflow.id}
key={workflow?.workflow.id}
workflowId={workflow?.workflow.id}
initialData={
workflow
? {
@ -362,21 +363,21 @@ function TenantSettingsContentTable() {
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-center p-4 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-2xl font-bold text-blue-600">
{workflow.workflow.totalSteps}
</div>
<div className="text-sm text-gray-600">Total Steps</div>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-center p-4 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-2xl font-bold text-green-600">
{workflow.workflow.activeSteps}
</div>
<div className="text-sm text-gray-600">Active Steps</div>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-center p-4 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div
className={`text-2xl font-bold ${
// workflow.workflow.requiresApproval
@ -387,9 +388,7 @@ function TenantSettingsContentTable() {
>
{
// workflow.workflow.requiresApproval
workflow.clientSettings.requiresApproval
? "Yes"
: "No"
workflow.clientSettings.requiresApproval ? "Yes" : "No"
}
</div>
<div className="text-sm text-gray-600">
@ -397,7 +396,7 @@ function TenantSettingsContentTable() {
</div>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-center p-4 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div
className={`text-2xl font-bold ${
// workflow.workflow.autoPublish
@ -425,14 +424,14 @@ function TenantSettingsContentTable() {
{workflow.steps.map((step: any, index: number) => (
<div
key={index}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
className="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-300 rounded-lg"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-blue-100 text-blue-600 rounded-full flex items-center justify-center text-sm font-medium">
{step.stepOrder}
</div>
<div>
<div className="font-medium dark:text-black">{step.stepName}</div>
<div className="font-medium text-black">{step.stepName}</div>
<div className="text-sm text-gray-500">
{step.conditionType &&
`Condition: ${step.conditionType}`}
@ -480,7 +479,7 @@ function TenantSettingsContentTable() {
<div className="mb-6">
<h4 className="text-lg font-medium mb-3">Client Settings</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Default Workflow
</div>
@ -488,7 +487,7 @@ function TenantSettingsContentTable() {
{workflow.clientSettings.defaultWorkflowName}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Auto Publish Articles
</div>
@ -504,7 +503,7 @@ function TenantSettingsContentTable() {
: "No"}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Requires Approval
</div>
@ -520,7 +519,7 @@ function TenantSettingsContentTable() {
: "No"}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Settings Active
</div>
@ -543,7 +542,7 @@ function TenantSettingsContentTable() {
Workflow Statistics
</h4>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Total Articles Processed
</div>
@ -551,7 +550,7 @@ function TenantSettingsContentTable() {
{workflow.statistics.totalArticlesProcessed}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Pending Articles
</div>
@ -559,7 +558,7 @@ function TenantSettingsContentTable() {
{workflow.statistics.pendingArticles}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Approved Articles
</div>
@ -567,7 +566,7 @@ function TenantSettingsContentTable() {
{workflow.statistics.approvedArticles}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Rejected Articles
</div>
@ -575,7 +574,7 @@ function TenantSettingsContentTable() {
{workflow.statistics.rejectedArticles}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Average Processing Time
</div>
@ -583,7 +582,7 @@ function TenantSettingsContentTable() {
{workflow.statistics.averageProcessingTime}h
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Most Active Step
</div>
@ -600,7 +599,7 @@ function TenantSettingsContentTable() {
Workflow Information
</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Client ID
</div>
@ -608,7 +607,7 @@ function TenantSettingsContentTable() {
{workflow.workflow.clientId}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Created At
</div>
@ -616,7 +615,7 @@ function TenantSettingsContentTable() {
{new Date(workflow.workflow.createdAt).toLocaleString()}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Updated At
</div>
@ -624,7 +623,7 @@ function TenantSettingsContentTable() {
{new Date(workflow.workflow.updatedAt).toLocaleString()}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Workflow ID
</div>
@ -632,7 +631,7 @@ function TenantSettingsContentTable() {
{workflow.workflow.id}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Has Branches
</div>
@ -646,7 +645,7 @@ function TenantSettingsContentTable() {
{workflow.workflow.hasBranches ? "Yes" : "No"}
</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="p-3 bg-gray-50 dark:bg-gray-300 rounded-lg">
<div className="text-sm font-medium text-gray-700 mb-1">
Max Step Order
</div>
@ -751,7 +750,7 @@ function TenantSettingsContentTable() {
onOpenChange={setIsHierarchyExpanded}
>
<CollapsibleTrigger asChild>
<CardHeader className="cursor-pointer hover:bg-gray-50 transition-colors">
<CardHeader className="cursor-pointer hover:bg-gray-50 dark:hover:bg-black transition-colors">
<CardTitle className="flex items-center justify-between">
<div className="flex items-center gap-2">
<UsersIcon className="h-5 w-5" />
@ -769,7 +768,7 @@ function TenantSettingsContentTable() {
<CardContent>
<div className="space-y-3">
{userLevels
.filter((ul) => !ul.parentLevelId) // Root levels
.filter((ul) => !ul.parentLevelId)
.sort((a, b) => a.levelNumber - b.levelNumber)
.map((rootLevel) => (
<div key={rootLevel.id} className="space-y-2">
@ -779,7 +778,7 @@ function TenantSettingsContentTable() {
{rootLevel.levelNumber}
</div>
<div className="flex-1">
<div className="font-medium">
<div className="font-medium text-black">
{rootLevel.name}
</div>
<div className="text-sm text-gray-500">
@ -814,7 +813,7 @@ function TenantSettingsContentTable() {
{childLevel.levelNumber}
</div>
<div className="flex-1">
<div className="font-medium text-sm">
<div className="font-medium text-sm text-black">
{childLevel.name}
</div>
<div className="text-xs text-gray-500">
@ -846,7 +845,7 @@ function TenantSettingsContentTable() {
)}
<Table className="overflow-hidden mt-3 mx-3">
<TableHeader className="sticky top-0 bg-white shadow-sm z-10">
<TableHeader className="sticky top-0 bg-white dark:bg-default-50 shadow-sm z-10">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (

View File

@ -1223,7 +1223,7 @@ export const UserLevelsForm: React.FC<UserLevelsFormProps> = ({
{menus.map((menu) => (
<label
key={menu.id}
className="flex items-start gap-3 p-3 border rounded-lg hover:bg-gray-50 cursor-pointer"
className="flex items-start gap-3 p-3 border rounded-lg hover:bg-gray-50 dark:hover:bg-black cursor-pointer"
>
<input
type="checkbox"
@ -1312,7 +1312,7 @@ export const UserLevelsForm: React.FC<UserLevelsFormProps> = ({
{actions.map((action) => (
<label
key={action.id}
className="flex items-start gap-3 p-2 border rounded-lg hover:bg-gray-50 cursor-pointer"
className="flex items-start gap-3 p-2 border rounded-lg hover:bg-gray-50 dark:hover:bg-black cursor-pointer"
>
<input
type="checkbox"

View File

@ -61,10 +61,10 @@ export default function CategoriesDetailForm() {
</div>
{/* Slug */}
<div className="space-y-2 py-3">
{/* <div className="space-y-2 py-3">
<Label>Slug</Label>
<Input type="text" value={detail.slug || "-"} readOnly />
</div>
</div> */}
{/* Description */}
<div className="space-y-2 py-3">
@ -89,7 +89,7 @@ export default function CategoriesDetailForm() {
</div>
{/* Tags */}
<div className="space-y-2 py-3 ">
{/* <div className="space-y-2 py-3 ">
<Label>Tags</Label>
<div className="flex flex-wrap gap-2 p-4 border rounded-sm">
{detail?.tags?.length > 0 ? (
@ -102,7 +102,7 @@ export default function CategoriesDetailForm() {
<span className="text-slate-400 text-sm">No tags</span>
)}
</div>
</div>
</div> */}
</Card>
{/* SIDEBAR */}
@ -122,7 +122,7 @@ export default function CategoriesDetailForm() {
<div>
<Label>Status</Label>
<p className="text-sm text-slate-600">
{detail.isPublish ? "Published" : "Draft"} |{" "}
{/* {detail.isPublish ? "Published" : "Draft"} |{" "} */}
{detail.isActive ? "Active" : "Inactive"}
</p>
</div>

View File

@ -60,7 +60,7 @@ export default function CategoriesUpdateForm() {
}, [id]);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const { name, value } = e.target;
setFormData((prev) => (prev ? { ...prev, [name]: value } : prev));
@ -94,7 +94,7 @@ export default function CategoriesUpdateForm() {
MySwal.fire(
"Error",
res?.message || "Gagal mengunggah thumbnail",
"error"
"error",
);
return null;
}
@ -186,7 +186,7 @@ export default function CategoriesUpdateForm() {
</div>
{/* Thumbnail Upload */}
<div className="space-y-2 py-3">
{/* <div className="space-y-2 py-3">
<Label>Thumbnail</Label>
{thumbnailPreview && (
<img
@ -203,18 +203,25 @@ export default function CategoriesUpdateForm() {
{isUploading && (
<p className="text-sm text-blue-500 mt-1">Mengunggah...</p>
)}
</div>
</div> */}
{/* Status */}
<div className="flex items-center gap-4 py-3">
<div className="flex items-center gap-2">
<Checkbox
{/* <Checkbox
checked={formData.isActive}
onCheckedChange={(checked) =>
handleCheckboxChange("isActive", Boolean(checked))
}
/> */}
{/* <input
type="checkbox"
checked={formData.isActive}
onChange={(e) =>
handleCheckboxChange("isActive", e.target.checked)
}
/>
<Label>Active</Label>
<Label>Active</Label> */}
</div>
</div>

View File

@ -362,7 +362,7 @@ const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
{ ssr: false },
);
interface FileWithPreview extends File {
@ -412,11 +412,11 @@ export default function FormVideo() {
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
null,
);
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [publishedForError, setPublishedForError] = useState<string | null>(
null
null,
);
const userId = Cookies.get("userId");
const [selectedSize, setSelectedSize] = useState("");
@ -478,7 +478,7 @@ export default function FormVideo() {
}
const filesWithPreview = acceptedFiles.map((file) =>
Object.assign(file, { preview: URL.createObjectURL(file) })
Object.assign(file, { preview: URL.createObjectURL(file) }),
);
setFiles((prev) => {
@ -503,12 +503,12 @@ export default function FormVideo() {
files.every(
(file: File) =>
["video/mp4", "video/mov", "video/avi"].includes(file.type) &&
file.size <= 100 * 1024 * 1024
file.size <= 100 * 1024 * 1024,
),
{
message:
"Hanya file .mp4, .mov, .avi, maksimal 100MB yang diperbolehkan.",
}
},
),
categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }),
tags: z
@ -722,7 +722,7 @@ export default function FormVideo() {
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
"",
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setArticleBody(cleanArticleBody || "");
@ -798,7 +798,7 @@ export default function FormVideo() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -829,7 +829,7 @@ export default function FormVideo() {
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
.map((opt: any) => opt.id),
);
}
} else {
@ -866,8 +866,8 @@ export default function FormVideo() {
const finalDescription = isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
@ -959,7 +959,7 @@ export default function FormVideo() {
MySwal.fire(
"Error",
response.message || "Failed to create article",
"error"
"error",
);
return false;
}
@ -987,7 +987,7 @@ export default function FormVideo() {
MySwal.fire(
"Error",
uploadResponse.message || "Failed to upload files",
"error"
"error",
);
return false;
}
@ -1003,17 +1003,17 @@ export default function FormVideo() {
try {
const thumbnailResponse = await uploadArticleThumbnail(
articleId,
thumbnailFormData
thumbnailFormData,
);
if (thumbnailResponse?.error) {
console.warn(
"Thumbnail upload failed:",
thumbnailResponse.message
thumbnailResponse.message,
);
} else {
console.log(
"Thumbnail uploaded successfully:",
thumbnailResponse
thumbnailResponse,
);
}
} catch (thumbnailError) {
@ -1025,7 +1025,7 @@ export default function FormVideo() {
MySwal.fire(
"Error",
"Failed to upload files. Please try again.",
"error"
"error",
);
return false;
}
@ -1076,7 +1076,7 @@ export default function FormVideo() {
idx: number,
id: string,
file: any,
duration: string
duration: string,
) {
console.log(idx, id, file, duration);
@ -1112,7 +1112,7 @@ export default function FormVideo() {
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
bytesTotal: any,
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
@ -1356,14 +1356,43 @@ export default function FormVideo() {
<div className="flex flex-row items-center gap-3 py-2">
<Label>Ai Assistance</Label>
<div className="flex items-center gap-3">
<Switch
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={isSwitchOn}
onChange={(e) => setIsSwitchOn(e.target.checked)}
className="sr-only peer"
/>
<div
className="
w-11 h-6
bg-gray-300
rounded-full
peer
peer-checked:bg-blue-600
transition-colors
after:content-['']
after:absolute
after:top-[2px]
after:left-[2px]
after:bg-white
after:rounded-full
after:h-5
after:w-5
after:transition-transform
peer-checked:after:translate-x-5
"
/>
</label>
{/* <Switch
defaultChecked={isSwitchOn}
color="primary"
id="c2"
onCheckedChange={(checked: boolean) =>
setIsSwitchOn(checked)
}
/>
/> */}
</div>
</div>
{isSwitchOn && (
@ -1622,14 +1651,12 @@ export default function FormVideo() {
<p className="text-sm font-semibold">Content Rewrite</p>
<div className="my-2">
<Button
size="sm"
type="button"
<button type="button"
onClick={handleRewriteClick}
className="bg-blue-500 text-white py-2 px-4 rounded"
>
Content Rewrite
</Button>
</button>
</div>
{showRewriteEditor && (
@ -1814,7 +1841,7 @@ export default function FormVideo() {
type="button"
onClick={() => {
const updatedTags = field.value.filter(
(_, i) => i !== index
(_, i) => i !== index,
);
field.onChange(updatedTags);
}}
@ -1852,19 +1879,19 @@ export default function FormVideo() {
? isAllChecked
: field.value.includes(option.id);
const handleChange = () => {
const handleChange = (checked: boolean) => {
let updated: string[] = [];
if (option.id === "all") {
updated = isAllChecked
? []
: options
updated = checked
? options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id);
.map((opt: any) => opt.id)
: [];
} else {
updated = isChecked
? field.value.filter((val) => val !== option.id)
: [...field.value, option.id];
updated = checked
? [...field.value, option.id]
: field.value.filter((val) => val !== option.id);
if (isAllChecked && option.id !== "all") {
updated = updated.filter((val) => val !== "all");
@ -1875,17 +1902,47 @@ export default function FormVideo() {
setPublishedFor(updated);
};
// const handleChange = () => {
// let updated: string[] = [];
// if (option.id === "all") {
// updated = isAllChecked
// ? []
// : options
// .filter((opt: any) => opt.id !== "all")
// .map((opt: any) => opt.id);
// } else {
// updated = isChecked
// ? field.value.filter((val) => val !== option.id)
// : [...field.value, option.id];
// if (isAllChecked && option.id !== "all") {
// updated = updated.filter((val) => val !== "all");
// }
// }
// field.onChange(updated);
// setPublishedFor(updated);
// };
return (
<div
key={option.id}
className="flex gap-2 items-center"
>
<Checkbox
<input
type="checkbox"
id={option.id}
checked={isChecked}
onChange={(e) => handleChange(e.target.checked)}
className="h-4 w-4 border border-gray-300 rounded text-blue-600 focus:ring-blue-500"
/>
{/* <Checkbox
id={option.id}
checked={isChecked}
onCheckedChange={handleChange}
className="border"
/>
/> */}
<Label htmlFor={option.id}>{option.label}</Label>
</div>
);

View File

@ -80,7 +80,7 @@ const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
{ ssr: false },
);
export default function FormImage() {
@ -113,7 +113,7 @@ export default function FormImage() {
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
null,
);
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] =
@ -169,17 +169,17 @@ export default function FormImage() {
.filter(
(file) =>
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
file.size <= MAX_FILE_SIZE
file.size <= MAX_FILE_SIZE,
)
.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
}),
);
if (validFiles.length === 0) {
toast.error(
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan."
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan.",
);
return;
}
@ -209,12 +209,12 @@ export default function FormImage() {
files.every(
(file: File) =>
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
file.size <= 100 * 1024 * 1024
file.size <= 100 * 1024 * 1024,
),
{
message:
"Hanya file .jpg, .jpeg, .png, maksimal 100MB yang diperbolehkan.",
}
},
),
categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }),
tags: z
@ -431,7 +431,7 @@ export default function FormImage() {
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
"",
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setArticleBody(cleanArticleBody || "");
@ -504,7 +504,7 @@ export default function FormImage() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -535,7 +535,7 @@ export default function FormImage() {
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
.map((opt: any) => opt.id),
);
}
} else {
@ -572,8 +572,8 @@ export default function FormImage() {
const finalDescription = isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
@ -628,7 +628,7 @@ export default function FormImage() {
MySwal.fire(
"Error",
response.message || "Failed to create article",
"error"
"error",
);
return false;
}
@ -647,7 +647,7 @@ export default function FormImage() {
MySwal.fire(
"Error",
uploadResponse.message || "Failed to upload files",
"error"
"error",
);
return false;
}
@ -663,7 +663,7 @@ export default function FormImage() {
MySwal.fire(
"Error",
"Failed to upload files. Please try again.",
"error"
"error",
);
return false;
}
@ -705,7 +705,7 @@ export default function FormImage() {
idx: number,
id: string,
file: any,
duration: string
duration: string,
) {
console.log(idx, id, file, duration);
@ -741,7 +741,7 @@ export default function FormImage() {
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
bytesTotal: any,
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
@ -996,14 +996,43 @@ export default function FormImage() {
<div className="flex flex-row items-center gap-3 py-3 ">
<Label>Ai Assistance</Label>
<div className="flex items-center gap-3">
<Switch
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={isSwitchOn}
onChange={(e) => setIsSwitchOn(e.target.checked)}
className="sr-only peer"
/>
<div
className="
w-11 h-6
bg-gray-300
rounded-full
peer
peer-checked:bg-blue-600
transition-colors
after:content-['']
after:absolute
after:top-[2px]
after:left-[2px]
after:bg-white
after:rounded-full
after:h-5
after:w-5
after:transition-transform
peer-checked:after:translate-x-5
"
/>
</label>
{/* <Switch
defaultChecked={isSwitchOn}
color="primary"
id="c2"
onCheckedChange={(checked: boolean) =>
setIsSwitchOn(checked)
}
/>
/> */}
</div>
</div>
{isSwitchOn && (
@ -1262,14 +1291,13 @@ export default function FormImage() {
<p className="text-sm font-semibold">Content Rewrite</p>
<div className="my-2">
<Button
size="sm"
<button
type="button"
onClick={handleRewriteClick}
className="bg-blue-500 text-white py-2 px-4 rounded"
className="bg-blue-500 text-white py-2 px-3 rounded hover:bg-black"
>
Content Rewrite
</Button>
</button>
</div>
{showRewriteEditor && (
@ -1498,20 +1526,20 @@ export default function FormImage() {
option.id === "all"
? isAllChecked
: field.value.includes(option.id);
const handleChange = () => {
const handleChange = (checked: boolean) => {
let updated: string[] = [];
if (option.id === "all") {
updated = isAllChecked
? []
: options
updated = checked
? options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id);
.map((opt: any) => opt.id)
: [];
} else {
updated = isChecked
? field.value.filter((val) => val !== option.id)
: [...field.value, option.id];
updated = checked
? [...field.value, option.id]
: field.value.filter((val) => val !== option.id);
if (isAllChecked && option.id !== "all") {
updated = updated.filter((val) => val !== "all");
@ -1522,17 +1550,48 @@ export default function FormImage() {
setPublishedFor(updated);
};
// const handleChange = () => {
// let updated: string[] = [];
// if (option.id === "all") {
// updated = isAllChecked
// ? []
// : options
// .filter((opt: any) => opt.id !== "all")
// .map((opt: any) => opt.id);
// } else {
// updated = isChecked
// ? field.value.filter((val) => val !== option.id)
// : [...field.value, option.id];
// if (isAllChecked && option.id !== "all") {
// updated = updated.filter((val) => val !== "all");
// }
// }
// field.onChange(updated);
// setPublishedFor(updated);
// };
return (
<div
key={option.id}
className="flex gap-2 items-center"
>
<Checkbox
<input
type="checkbox"
id={option.id}
checked={isChecked}
onChange={(e) => handleChange(e.target.checked)}
className="h-4 w-4 border border-gray-300 rounded text-blue-600 focus:ring-blue-500"
/>
{/* <Checkbox
id={option.id}
checked={isChecked}
onCheckedChange={handleChange}
className="border"
/>
/> */}
<Label htmlFor={option.id}>{option.label}</Label>
</div>
);

View File

@ -13,6 +13,7 @@ import { Link } from "@/i18n/routing";
import { DynamicLogoTenant } from "./dynamic-logo-tenant";
import { useTranslations } from "next-intl";
import LocalSwitcher from "../partials/header/locale-switcher";
import ThemeSwitcher from "../partials/header/theme-switcher";
export default function Navbar() {
const t = useTranslations("Navbar");
@ -121,7 +122,7 @@ export default function Navbar() {
isDropdownOpen ||
pathname.startsWith("/public/publication")
? "text-black"
: ""
: "",
)}
>
{item.label}
@ -131,7 +132,7 @@ export default function Navbar() {
isDropdownOpen ||
pathname.startsWith("/public/publication")
? "opacity-100"
: "opacity-0"
: "opacity-0",
)}
/>
</button>
@ -156,7 +157,7 @@ export default function Navbar() {
onClick={handleClick}
className={cn(
"relative text-gray-500 hover:text-black transition-colors",
isActive && "text-black"
isActive && "text-black",
)}
>
{item.label}
@ -170,6 +171,10 @@ export default function Navbar() {
})}
</nav>
{/* <div className="hidden custom-lg-button:flex text-left">
<ThemeSwitcher />
</div> */}
{/* 🔹 PROFILE / LOGIN SECTION */}
<nav className="hidden md:flex items-center gap-3 z-10 relative">
{!isLoggedIn ? (
@ -180,7 +185,9 @@ export default function Navbar() {
</Button>
</Link>
<Link href="/auth">
<Button className="bg-red-700 text-white cursor-pointer hover:bg-white hover:border hover:border-red-700 hover:text-red-700">{t("login")}</Button>
<Button className="bg-red-700 text-white cursor-pointer hover:bg-white hover:border hover:border-red-700 hover:text-red-700">
{t("login")}
</Button>
</Link>
</>
) : (

View File

@ -125,7 +125,7 @@ export default function DashboardContainer() {
return (
<div className="space-y-8">
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-4 p-2">
{/* User Profile Card */}
<motion.div
className="col-span-1 md:col-span-2 bg-white rounded-2xl shadow-md border border-gray-200 p-4 hover:shadow-lg transition-all duration-300"
@ -135,11 +135,11 @@ export default function DashboardContainer() {
>
<div className="flex justify-between items-start">
<div className="space-y-2">
<p className="text-gray-600 font-medium">Welcome back,</p>
<p className="text-xl font-semibold text-gray-900">{username}</p>
<p className="text-sm text-gray-500">Admin Dashboard</p>
<p className="text-gray-600 dark:text-black font-medium">Welcome back,</p>
<p className="text-xl font-semibold text-gray-900 dark:text-black ">{username}</p>
<p className="text-sm text-gray-500 dark:text-black ">Admin Dashboard</p>
</div>
<div className="p-3 bg-gradient-to-br from-blue-50 to-purple-50 rounded-xl">
<div className="p-3 bg-gradient-to-br from-blue-50 to-purple-50 rounded-xl dark:text-black ">
<DashboardUserIcon size={60} />
</div>
</div>
@ -153,7 +153,7 @@ export default function DashboardContainer() {
transition={{ delay: 0.2 }}
>
<div className="flex flex-col items-center text-center space-y-3">
<div className="p-3 bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl">
<div className="p-3 bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl dark:text-black">
<DashboardSpeecIcon />
</div>
<div>
@ -173,7 +173,7 @@ export default function DashboardContainer() {
transition={{ delay: 0.3 }}
>
<div className="flex flex-col items-center text-center space-y-3">
<div className="p-3 bg-gradient-to-br from-blue-50 to-cyan-50 rounded-xl">
<div className="p-3 bg-gradient-to-br from-blue-50 to-cyan-50 rounded-xl dark:text-black">
<DashboardConnectIcon />
</div>
<div>
@ -193,7 +193,7 @@ export default function DashboardContainer() {
transition={{ delay: 0.4 }}
>
<div className="flex flex-col items-center text-center space-y-3">
<div className="p-3 bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl">
<div className="p-3 bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl dark:text-black">
<DashboardShareIcon />
</div>
<div>
@ -213,7 +213,7 @@ export default function DashboardContainer() {
transition={{ delay: 0.5 }}
>
<div className="flex flex-col items-center text-center space-y-3">
<div className="p-3 bg-gradient-to-br from-orange-50 to-red-50 rounded-xl">
<div className="p-3 bg-gradient-to-br from-orange-50 to-red-50 rounded-xl dark:text-black">
<DashboardCommentIcon size={40} />
</div>
<div>
@ -227,7 +227,7 @@ export default function DashboardContainer() {
</div>
{/* Content Section */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 p-2">
{/* Analytics Chart */}
<motion.div
className="bg-white rounded-2xl shadow-md border border-gray-200 p-4"
@ -306,12 +306,12 @@ export default function DashboardContainer() {
))}
</div>
<div className="mt-6 flex justify-center">
{/* <div className="mt-6 flex justify-center">
<CustomPagination
totalPage={totalPage}
onPageChange={(data) => setPage(data)}
/>
</div>
</div> */}
</motion.div>
</div>
</div>

View File

@ -1,6 +1,6 @@
import axios from "axios";
const baseURL = "https://disestages.com/api";
const baseURL = "https://new.disestages.com/api";
const axiosNulisAIInstance = axios.create({
baseURL,