Merge pull request #421 from bipproduction/amalia/15-apr-25

Amalia/15 apr 25
This commit is contained in:
Amalia
2025-04-15 17:17:40 +08:00
committed by GitHub
8 changed files with 241 additions and 38 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -29,7 +29,12 @@
"@mantine/tiptap": "^7.11.0",
"@prisma/client": "5.16.1",
"@tabler/icons-react": "^3.7.0",
"@tiptap/extension-color": "^2.11.7",
"@tiptap/extension-highlight": "^2.11.7",
"@tiptap/extension-link": "^2.4.0",
"@tiptap/extension-text-align": "^2.11.7",
"@tiptap/extension-underline": "^2.11.7",
"@tiptap/pm": "^2.11.7",
"@tiptap/react": "^2.4.0",
"@tiptap/starter-kit": "^2.4.0",
"@types/lodash": "^4.17.6",

View File

@@ -3,6 +3,7 @@ import { WrapLayout } from "@/module/_global"
import { funDetectCookies, funGetUserByCookies } from "@/module/auth"
import _ from "lodash"
import { redirect } from "next/navigation"
import '@mantine/tiptap/styles.css';
export default async function Layout({ children }: { children: React.ReactNode }) {
const cookies = await funDetectCookies()

View File

@@ -7,7 +7,7 @@ import { IoCloseOutline } from 'react-icons/io5';
import { TEMA } from '../bin/val_global';
export default function NotificationCustomeCenter({ title, desc, onClick }: { title: string, desc: string, onClick: (val: string) => void, }) {
const [opened, setOpened] = useState(false);
const [opened, setOpened] = useState(false)
const tema = useHookstate(TEMA)
useShallowEffect(() => {
@@ -72,7 +72,15 @@ export default function NotificationCustomeCenter({ title, desc, onClick }: { ti
/>
<Text size="xl" fw={500} ta="center">PENGUMUMAN</Text>
<Text size="sm" ta="center">{desc}</Text>
<SimpleGrid cols={2} spacing="xs" w={'90%'}>
<SimpleGrid
cols={{ base: 1, sm: 2, lg: 2 }}
spacing="xs"
w={'90%'}>
{/* {
isMobile
? <></>
:
<> */}
<Button
fullWidth
radius="md"
@@ -93,6 +101,8 @@ export default function NotificationCustomeCenter({ title, desc, onClick }: { ti
>
Tandai Telah Dibaca
</Button>
{/* </>
} */}
</SimpleGrid>
</Flex>
</Box>

View File

@@ -2,8 +2,16 @@
import { keyWibu, LayoutNavbarNew, TEMA } from "@/module/_global";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useHookstate } from "@hookstate/core";
import { Box, Button, Flex, Group, rem, Stack, Text, Textarea, TextInput } from "@mantine/core";
import { Box, Button, Flex, Group, rem, Stack, Text, TextInput } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { Link, RichTextEditor } from "@mantine/tiptap";
import { Color } from '@tiptap/extension-color';
import Highlight from '@tiptap/extension-highlight';
import TextAlign from '@tiptap/extension-text-align';
import TextStyle from '@tiptap/extension-text-style';
import Underline from '@tiptap/extension-underline';
import { useEditor } from "@tiptap/react";
import StarterKit from '@tiptap/starter-kit';
import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
@@ -36,6 +44,19 @@ export default function CreateAnnouncement() {
desc: false
});
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Highlight,
TextStyle,
Color,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content: "",
});
async function onSubmit() {
try {
@@ -74,14 +95,17 @@ export default function CreateAnnouncement() {
function onCheck() {
const cek = checkAll()
if (!cek)
return false
onValidation('desc', '')
setTimeout(() => {
const cek = checkAll()
if (!cek)
return false
if (memberValue.length == 0)
return toast.error("Error! silahkan pilih divisi")
if (memberValue.length == 0)
return toast.error("Error! silahkan pilih divisi")
setOpen(true)
setOpen(true)
}, 500)
}
function checkAll() {
@@ -90,14 +114,16 @@ export default function CreateAnnouncement() {
setTouched(touched => ({ ...touched, title: true }))
nilai = false
}
if (isData.desc === "") {
if (String(editor?.getHTML()) === "" || String(editor?.getHTML()) === "<p></p>" || String(editor?.getHTML()) === "undefined") {
setTouched(touched => ({ ...touched, desc: true }))
nilai = false
toast.error("Pengumuman Tidak Boleh Kosong!")
}
return nilai
}
function onValidation(kategori: string, val: string) {
if (kategori == 'title') {
setisData({ ...isData, title: val })
@@ -107,15 +133,16 @@ export default function CreateAnnouncement() {
setTouched({ ...touched, title: false })
}
} else if (kategori == 'desc') {
setisData({ ...isData, desc: val })
if (val === "") {
setTouched({ ...touched, desc: true })
} else {
setTouched({ ...touched, desc: false })
}
setisData({ ...isData, desc: String(editor?.getHTML()) })
// if (val === "") {
// setTouched({ ...touched, desc: true })
// } else {
// setTouched({ ...touched, desc: false })
// }
}
}
if (isChooseMember) return <CreateUsersAnnouncement onClose={() => { setIsChooseMember(false) }} />
return (
@@ -141,7 +168,7 @@ export default function CreateAnnouncement() {
)
}
/>
<Textarea
{/* <Textarea
size="md"
radius={10}
w={"100%"}
@@ -162,7 +189,75 @@ export default function CreateAnnouncement() {
isData.desc == "" ? "Pengumuman Tidak Boleh Kosong" : null
)
}
/>
/> */}
<Box>
<Group gap="xs">
<Text>Pengumuman</Text>
<Text c={'red'}>*</Text>
</Group>
<RichTextEditor editor={editor} style={{ border: `1px solid ${tema.get().utama}` }}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.Highlight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ColorPicker
colors={[
'#25262b',
'#868e96',
'#fa5252',
'#e64980',
'#be4bdb',
'#7950f2',
'#4c6ef5',
'#228be6',
'#15aabf',
'#12b886',
'#40c057',
'#82c91e',
'#fab005',
'#fd7e14',
]}
/>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Color color="#F03E3E" />
<RichTextEditor.Color color="#7048E8" />
<RichTextEditor.Color color="#1098AD" />
<RichTextEditor.Color color="#37B24D" />
<RichTextEditor.Color color="#F59F00" />
</RichTextEditor.ControlsGroup>
<RichTextEditor.UnsetColor />
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
</Box>
<Box pt={10}>
<Group justify="space-between" style={{
border: `1px solid ${tema.get().utama}`,

View File

@@ -95,7 +95,7 @@ export default function DetailAnnouncement({ id }: { id: string }) {
</Grid.Col>
<Grid.Col span={10}>
<Spoiler maxHeight={100} showLabel="Lebih banyak" hideLabel="Lebih sedikit">
<Text>{isData?.desc}</Text>
<Box dangerouslySetInnerHTML={{ __html: String(isData?.desc) }} />
</Spoiler>
</Grid.Col>
</Grid>

View File

@@ -2,8 +2,16 @@
import { LayoutNavbarNew, TEMA } from "@/module/_global";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useHookstate } from "@hookstate/core";
import { Box, Button, Flex, Group, List, rem, Skeleton, Stack, Text, Textarea, TextInput } from "@mantine/core";
import { Box, Button, Flex, Group, List, rem, Skeleton, Stack, Text, TextInput } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { Link, RichTextEditor } from "@mantine/tiptap";
import Color from "@tiptap/extension-color";
import Highlight from '@tiptap/extension-highlight';
import TextAlign from "@tiptap/extension-text-align";
import TextStyle from "@tiptap/extension-text-style";
import Underline from "@tiptap/extension-underline";
import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
@@ -15,7 +23,6 @@ import EditChooseMember from "./edit_choose_member";
export default function EditAnnouncement() {
const [isOpen, setOpen] = useState(false)
const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false)
const [isChooseDivisi, setChooseDivisi] = useState(false)
const param = useParams<{ id: string }>()
const [loading, setLoading] = useState(true)
@@ -32,6 +39,19 @@ export default function EditAnnouncement() {
})
const memberGroup = useHookstate(globalMemberEditAnnouncement)
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Highlight,
TextStyle,
Color,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content: body.desc,
});
async function fetchOneAnnouncement() {
try {
@@ -60,16 +80,11 @@ export default function EditAnnouncement() {
})
arrNew.push(newObject)
})
memberGroup.set(arrNew)
} else {
toast.error(res.message)
}
setLoading(false)
} catch (error) {
console.error(error)
toast.error("Gagal mendapatkan pengumuman, coba lagi nanti")
@@ -84,12 +99,16 @@ export default function EditAnnouncement() {
fetchOneAnnouncement()
}, [])
useShallowEffect(() => {
editor?.commands.insertContent(body.desc)
}, [editor, body.desc])
async function onSubmit() {
try {
setLoadingSubmit(true)
const response = await funEditAnnouncement(param.id, {
title: body.title,
desc: body.desc,
desc: String(editor?.getHTML()),
groups: memberGroup.get() as GroupData[]
})
@@ -109,10 +128,10 @@ export default function EditAnnouncement() {
}
function onCheck() {
if (Object.values(touched).some((v) => v == true))
return false
if (Object.values(touched).some((v) => v == true) || String(editor?.getHTML()) === "" || String(editor?.getHTML()) === "<p></p>" || String(editor?.getHTML()) === "undefined")
return toast.error("Error! Judul dan pengumuman tidak boleh kosong")
if (memberGroup.get().length == 0)
return toast.error("Error! silahkan pilih divisi")
return toast.error("Error! silahkan pilih divisi")
setOpen(true)
}
@@ -126,8 +145,7 @@ export default function EditAnnouncement() {
setTouched({ ...touched, title: false })
}
} else if (kategori == 'desc') {
setBody({ ...body, desc: val })
if (val === "") {
if (String(editor?.getHTML()) === "" || String(editor?.getHTML()) === "<p></p>" || String(editor?.getHTML()) === "undefined") {
setTouched({ ...touched, desc: true })
} else {
setTouched({ ...touched, desc: false })
@@ -166,14 +184,16 @@ export default function EditAnnouncement() {
},
}}
value={body.title}
onChange={(e) => { onValidation('title', e.target.value) }}
onChange={(e) => {
onValidation('title', e.target.value)
}}
error={
touched.title && (
body.title == "" ? "Judul Tidak Boleh Kosong" : null
)
}
/>
<Textarea
{/* <Textarea
size="md"
radius={10}
w={"100%"}
@@ -194,7 +214,76 @@ export default function EditAnnouncement() {
body.desc == "" ? "Pengumuman Tidak Boleh Kosong" : null
)
}
/>
/> */}
<Box>
<Group gap="xs">
<Text>Pengumuman</Text>
<Text c={'red'}>*</Text>
</Group>
<RichTextEditor editor={editor} style={{ border: `1px solid ${tema.get().utama}` }}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.Highlight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ColorPicker
colors={[
'#25262b',
'#868e96',
'#fa5252',
'#e64980',
'#be4bdb',
'#7950f2',
'#4c6ef5',
'#228be6',
'#15aabf',
'#12b886',
'#40c057',
'#82c91e',
'#fab005',
'#fd7e14',
]}
/>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Color color="#F03E3E" />
<RichTextEditor.Color color="#7048E8" />
<RichTextEditor.Color color="#1098AD" />
<RichTextEditor.Color color="#37B24D" />
<RichTextEditor.Color color="#F59F00" />
</RichTextEditor.ControlsGroup>
<RichTextEditor.UnsetColor />
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
</Box>
<Box pt={10} w={"100%"}>
<Group justify="space-between" style={{
border: `1px solid ${tema.get().utama}`,

View File

@@ -182,9 +182,12 @@ export default function ListAnnouncement() {
</Grid>
{/* <Text c={tema.get().utama} lineClamp={2}>{v.desc}</Text> */}
<Spoiler maxHeight={50} showLabel="Lebih banyak" hideLabel="Lebih sedikit">
<Text c={tema.get().utama} onClick={() => {
router.push(`/announcement/${v.id}`)
}} >{v.desc}</Text>
<Box
dangerouslySetInnerHTML={{ __html: String(v.desc) }}
onClick={() => {
router.push(`/announcement/${v.id}`)
}}
/>
</Spoiler>
</Grid.Col>
</Grid>