diff --git a/apps/frontend/app/admin/problem/_components/VisibleForm.tsx b/apps/frontend/app/admin/_components/VisibleForm.tsx
similarity index 96%
rename from apps/frontend/app/admin/problem/_components/VisibleForm.tsx
rename to apps/frontend/app/admin/_components/VisibleForm.tsx
index b408099a48..b2244043b0 100644
--- a/apps/frontend/app/admin/problem/_components/VisibleForm.tsx
+++ b/apps/frontend/app/admin/_components/VisibleForm.tsx
@@ -2,7 +2,7 @@
import { useFormContext, useController } from 'react-hook-form'
import { FaEye, FaEyeSlash } from 'react-icons/fa'
-import ErrorMessage from '../../_components/ErrorMessage'
+import ErrorMessage from './ErrorMessage'
export default function VisibleForm({
blockEdit = false
diff --git a/apps/frontend/app/admin/notice/[noticeId]/page.tsx b/apps/frontend/app/admin/notice/[noticeId]/page.tsx
new file mode 100644
index 0000000000..4d33d6d194
--- /dev/null
+++ b/apps/frontend/app/admin/notice/[noticeId]/page.tsx
@@ -0,0 +1,39 @@
+'use client'
+
+import KatexContent from '@/components/KatexContent'
+import { ScrollArea, ScrollBar } from '@/components/shadcn/scroll-area'
+import { GET_NOTICE } from '@/graphql/notice/queries'
+import { useQuery } from '@apollo/client'
+import Link from 'next/link'
+import { FaAngleLeft } from 'react-icons/fa6'
+
+export default function Page({ params }: { params: { noticeId: string } }) {
+ const { noticeId } = params
+
+ const noticeData = useQuery(GET_NOTICE, {
+ variables: {
+ groupId: 1,
+ noticeId: Number(noticeId)
+ }
+ }).data?.getNotice
+
+ return (
+
+
+
+
+
+
+
+ {noticeData?.title}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/frontend/app/admin/notice/_components/CreateNoticeForm.tsx b/apps/frontend/app/admin/notice/_components/CreateNoticeForm.tsx
new file mode 100644
index 0000000000..9facaf5186
--- /dev/null
+++ b/apps/frontend/app/admin/notice/_components/CreateNoticeForm.tsx
@@ -0,0 +1,58 @@
+'use client'
+
+import { CREATE_NOTICE } from '@/graphql/notice/mutation'
+import { useMutation } from '@apollo/client'
+import type { CreateNoticeInput } from '@generated/graphql'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { useRouter } from 'next/navigation'
+import type { ReactNode } from 'react'
+import { FormProvider, useForm } from 'react-hook-form'
+import { toast } from 'sonner'
+import { createSchema } from '../_libs/schemas'
+
+interface CreateNoticeFormProps {
+ children: ReactNode
+}
+
+export default function CreateNoticeForm({ children }: CreateNoticeFormProps) {
+ const methods = useForm
({
+ resolver: zodResolver(createSchema),
+ defaultValues: {
+ title: '',
+ content: '',
+ isFixed: false,
+ isVisible: true
+ }
+ })
+ const router = useRouter()
+
+ const [createNotice] = useMutation(CREATE_NOTICE, {
+ onError: () => {
+ toast.error('Failed to create problem')
+ },
+ onCompleted: (data) => {
+ const noticeId = data.createNotice.id
+ toast.success('Notice created successfully')
+ router.push(`/admin/notice/${noticeId}`)
+ router.refresh()
+ }
+ })
+
+ const onSubmit = methods.handleSubmit(async () => {
+ const noticeInput = methods.getValues()
+ await createNotice({
+ variables: {
+ groupId: 1,
+ noticeInput
+ }
+ })
+ })
+
+ return (
+ <>
+
+ >
+ )
+}
diff --git a/apps/frontend/app/admin/notice/_components/FixedForm.tsx b/apps/frontend/app/admin/notice/_components/FixedForm.tsx
new file mode 100644
index 0000000000..e3d5d8c3b8
--- /dev/null
+++ b/apps/frontend/app/admin/notice/_components/FixedForm.tsx
@@ -0,0 +1,54 @@
+'use client'
+
+import { useFormContext, useController } from 'react-hook-form'
+import { TbPinned, TbPinnedOff } from 'react-icons/tb'
+import ErrorMessage from '../../_components/ErrorMessage'
+
+export default function FixedForm({
+ blockEdit = false
+}: {
+ blockEdit?: boolean
+}) {
+ const {
+ control,
+ formState: { errors }
+ } = useFormContext()
+
+ const { field: isFixedField } = useController({
+ name: 'isFixed',
+ control,
+ defaultValue: true
+ })
+
+ return (
+ <>
+
+ {errors.isFixed && }
+ >
+ )
+}
diff --git a/apps/frontend/app/admin/notice/_libs/schemas.ts b/apps/frontend/app/admin/notice/_libs/schemas.ts
new file mode 100644
index 0000000000..45be066af6
--- /dev/null
+++ b/apps/frontend/app/admin/notice/_libs/schemas.ts
@@ -0,0 +1,8 @@
+import { z } from 'zod'
+
+export const createSchema = z.object({
+ title: z.string().min(1),
+ content: z.string().min(1),
+ isFixed: z.boolean(),
+ isVisible: z.boolean()
+})
diff --git a/apps/frontend/app/admin/notice/create/page.tsx b/apps/frontend/app/admin/notice/create/page.tsx
new file mode 100644
index 0000000000..b22dce61c8
--- /dev/null
+++ b/apps/frontend/app/admin/notice/create/page.tsx
@@ -0,0 +1,55 @@
+import { Button } from '@/components/shadcn/button'
+import { ScrollArea, ScrollBar } from '@/components/shadcn/scroll-area'
+import Link from 'next/link'
+import { FaAngleLeft } from 'react-icons/fa6'
+import { IoMdCheckmarkCircleOutline } from 'react-icons/io'
+import DescriptionForm from '../../_components/DescriptionForm'
+import FormSection from '../../_components/FormSection'
+import TitleForm from '../../_components/TitleForm'
+import VisibleForm from '../../_components/VisibleForm'
+import CreateNoticeForm from '../_components/CreateNoticeForm'
+import FixedForm from '../_components/FixedForm'
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+ Create Notice
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/frontend/app/admin/problem/[problemId]/edit/page.tsx b/apps/frontend/app/admin/problem/[problemId]/edit/page.tsx
index d62300b74a..c4e6441894 100644
--- a/apps/frontend/app/admin/problem/[problemId]/edit/page.tsx
+++ b/apps/frontend/app/admin/problem/[problemId]/edit/page.tsx
@@ -19,13 +19,13 @@ import DescriptionForm from '../../../_components/DescriptionForm'
import FormSection from '../../../_components/FormSection'
import SwitchField from '../../../_components/SwitchField'
import TitleForm from '../../../_components/TitleForm'
+import VisibleForm from '../../../_components/VisibleForm'
import { CautionDialog } from '../../_components/CautionDialog'
import InfoForm from '../../_components/InfoForm'
import LimitForm from '../../_components/LimitForm'
import PopoverVisibleInfo from '../../_components/PopoverVisibleInfo'
import TemplateField from '../../_components/TemplateField'
import TestcaseField from '../../_components/TestcaseField'
-import VisibleForm from '../../_components/VisibleForm'
import { editSchema } from '../../_libs/schemas'
import { validateScoreWeight } from '../../_libs/utils'
import { ScoreCautionDialog } from './_components/ScoreCautionDialog'
diff --git a/apps/frontend/app/admin/problem/create/page.tsx b/apps/frontend/app/admin/problem/create/page.tsx
index 1086648b09..c85b654208 100644
--- a/apps/frontend/app/admin/problem/create/page.tsx
+++ b/apps/frontend/app/admin/problem/create/page.tsx
@@ -8,12 +8,12 @@ import DescriptionForm from '../../_components/DescriptionForm'
import FormSection from '../../_components/FormSection'
import SwitchField from '../../_components/SwitchField'
import TitleForm from '../../_components/TitleForm'
+import VisibleForm from '../../_components/VisibleForm'
import InfoForm from '../_components/InfoForm'
import LimitForm from '../_components/LimitForm'
import PopoverVisibleInfo from '../_components/PopoverVisibleInfo'
import TemplateField from '../_components/TemplateField'
import TestcaseField from '../_components/TestcaseField'
-import VisibleForm from '../_components/VisibleForm'
import CreateProblemForm from './_components/CreateProblemForm'
export default function Page() {
diff --git a/apps/frontend/graphql/notice/mutation.ts b/apps/frontend/graphql/notice/mutation.ts
new file mode 100644
index 0000000000..1c640c3a64
--- /dev/null
+++ b/apps/frontend/graphql/notice/mutation.ts
@@ -0,0 +1,11 @@
+import { gql } from '@generated'
+
+const CREATE_NOTICE = gql(`
+ mutation CreateNotice($groupId: Int!, $noticeInput: CreateNoticeInput!) {
+ createNotice(groupId: $groupId, input: $noticeInput) {
+ id
+ }
+ }
+`)
+
+export { CREATE_NOTICE }
diff --git a/apps/frontend/graphql/notice/queries.ts b/apps/frontend/graphql/notice/queries.ts
new file mode 100644
index 0000000000..529246e4c2
--- /dev/null
+++ b/apps/frontend/graphql/notice/queries.ts
@@ -0,0 +1,12 @@
+import { gql } from '@apollo/client'
+
+const GET_NOTICE = gql(`
+ query GetNotice($groupId: Int!, $noticeId: Int!) {
+ getNotice(groupId: $groupId, noticeId: $noticeId) {
+ title
+ content
+ }
+ }
+`)
+
+export { GET_NOTICE }