import { useCallback, useEffect, useState } from 'react' import { getResetPreview, resetChannels, RESET_CONFIRM_PHRASE, type ResetReport, type ResetTarget, } from '../api/client' import { useStages } from '../codes/stage' import HelpButton from '../help/HelpButton' /** * 시스템관리 화면. 지금은 [채널 초기화] 하나만 있다. * * 되돌리기 번거로운 작업이라 3중으로 막는다. * 1) 무엇이 사라지는지 목록으로 먼저 보여준다 * 2) 확인 문구를 그대로 입력해야 버튼이 열린다 * 3) 서버도 같은 문구를 다시 검사한다 * * 지우는 범위는 웹앱이 만든(=DB가 채널 ID를 들고 있는) 채널뿐이다. Mattermost 팀에는 * 엑셀 VBA로 만든 예전 채널과 공용 채널(Town Square/FAQ/대쉬보드/양식 모음/자료실)이 함께 * 있어서, 이름으로 훑어 지우면 그것들까지 날아간다. 사용자 계정도 건드리지 않는다. */ interface Props { /** 초기화가 끝나면 상위(App)가 기관 목록을 다시 읽도록. */ onChanged?: () => void } export default function SystemPage({ onChanged }: Props) { const stages = useStages() const [targets, setTargets] = useState(null) const [error, setError] = useState(null) const [confirm, setConfirm] = useState('') const [busy, setBusy] = useState(false) const [report, setReport] = useState(null) const [dialogOpen, setDialogOpen] = useState(false) const reload = useCallback(async () => { try { setTargets(await getResetPreview()) setError(null) } catch (e) { setError((e as Error).message) } }, []) useEffect(() => { void reload() }, [reload]) const channelCount = (targets ?? []).reduce((sum, t) => sum + t.channels.length, 0) const canRun = confirm.trim() === RESET_CONFIRM_PHRASE && channelCount > 0 && !busy async function run() { setBusy(true) setDialogOpen(false) try { const result = await resetChannels(confirm.trim()) setReport(result) setConfirm('') await reload() onChanged?.() setError(null) } catch (e) { setError((e as Error).message) } finally { setBusy(false) } } return (

시스템관리

운영 중 되돌리기가 필요한 작업을 모아 둔다.

채널 초기화

  • · 웹앱이 만든 Mattermost 채널을 보관(아카이브) 처리한다. 영구 삭제가 아니라 시스템 콘솔에서 되돌릴 수 있다.
  • · 기관의 채널 연결과 진행단계가 비워져, 채널관리에서 처음부터 다시 만들 수 있게 된다.
  • · 담당자 배정, 권리확인·권리처리 자료, 업무메모는 그대로 남는다.
  • · 공용 채널(Town Square·FAQ·대쉬보드·양식 모음·자료실)과 엑셀로 만든 예전 채널은 대상이 아니다.
  • · 사용자 계정도 건드리지 않는다.
{error && (

{error}

)} {report && (

채널 {report.archivedChannels}개 보관, 기관 {report.clearedOrgs}곳 초기화 {report.failures.length > 0 && `, 실패 ${report.failures.length}건`}

{report.failures.length > 0 && (
    {report.failures.map((f) => (
  • · {f}
  • ))}
)}
)}

대상 {targets?.length ?? 0}개 기관 · 채널 {channelCount}개

{targets === null ? (

불러오는 중…

) : targets.length === 0 ? (

초기화할 채널이 없습니다.

) : (
{targets.map((t) => ( ))}
연번 기관명 현재 단계 보관될 채널
{t.orgNo} {t.orgName} {t.stage === null ? '-' : `${t.stage}. ${stages.label(t.stage)}`} {t.channels.map((c) => c.kind).join(' · ')}
)}
setConfirm(e.target.value)} className="w-40 rounded-md border border-gray-300 px-2 py-1.5 text-sm" />
{dialogOpen && (

정말 초기화할까요?

기관 {targets?.length ?? 0}곳의 채널 {channelCount}개가 Mattermost에서 보관 처리되고, 해당 기관의 채널 연결과 진행단계가 비워집니다.

)}
) }