feat: add Mattermost bot integration and user notification configuration settings

This commit is contained in:
Firman Ramdhani
2026-09-16 17:18:55 +07:00
parent 7c3c5e2249
commit 2814e3cab8
5 changed files with 348 additions and 11 deletions
+83 -5
View File
@@ -1,7 +1,7 @@
'use client'
import { useEffect, useState } from 'react'
import { getSessionUser, updateProfile, changePassword } from '@/app/actions'
import { getSessionUser, updateProfile, changePassword, getSettings, getUserNotificationConfig, updateUserNotificationConfig } from '@/app/actions'
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
@@ -21,6 +21,7 @@ import {
UserCircle,
KeyRound,
LockKeyhole,
MessageCircle,
} from 'lucide-react'
export default function ProfilePage() {
@@ -29,6 +30,13 @@ export default function ProfilePage() {
const [photo, setPhoto] = useState('')
const [loading, setLoading] = useState(true)
// Notification Config state
const [globalNotifEnabled, setGlobalNotifEnabled] = useState(false)
const [mmChannelId, setMmChannelId] = useState('')
const [mmActive, setMmActive] = useState(true)
const [savingNotif, setSavingNotif] = useState(false)
const [notifSuccess, setNotifSuccess] = useState(false)
// Profile state
const [savingProfile, setSavingProfile] = useState(false)
const [profileError, setProfileError] = useState('')
@@ -54,6 +62,20 @@ export default function ProfilePage() {
setUser(sessionUser)
setName(sessionUser.name)
setPhoto(sessionUser.photo || '')
const [globalSettings, notifConfig] = await Promise.all([
getSettings(),
getUserNotificationConfig(sessionUser.id)
])
if (globalSettings?.MATTERMOST_NOTIF_ENABLED === 'true') {
setGlobalNotifEnabled(true)
}
if (notifConfig?.success && notifConfig.config) {
setMmChannelId(notifConfig.config.mattermost_channel_id || '')
setMmActive(notifConfig.config.is_active ?? true)
}
}
setLoading(false)
}
@@ -69,14 +91,20 @@ export default function ProfilePage() {
setProfileSuccess(false)
setSavingProfile(true)
const res = await updateProfile(user.id, name.trim(), photo.trim() || null)
const [res, notifRes] = await Promise.all([
updateProfile(user.id, name.trim(), photo.trim() || null),
globalNotifEnabled ? updateUserNotificationConfig(user.id, {
mattermost_channel_id: mmChannelId,
is_active: mmActive
}) : Promise.resolve({ success: true, error: null })
])
if (res.success) {
if (res.success && notifRes.success) {
setUser(res.user)
setProfileSuccess(true)
setTimeout(() => setProfileSuccess(false), 3000)
} else {
setProfileError(res.error || 'Terjadi kesalahan')
setProfileError(res.error || notifRes.error || 'Terjadi kesalahan')
}
setSavingProfile(false)
}
@@ -215,6 +243,55 @@ export default function ProfilePage() {
/>
</div>
{globalNotifEnabled && (
<div className="space-y-6 pt-6 mt-6 border-t border-slate-100 dark:border-slate-800">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-purple-100 text-purple-600 dark:bg-purple-900/30 dark:text-purple-400">
<MessageCircle className="h-5 w-5" />
</div>
<div>
<h3 className="text-sm font-bold text-slate-900 dark:text-white">
Notifikasi Mattermost
</h3>
<p className="mt-0.5 text-[11px] text-slate-500">
Terima update pesanan PO langsung di DM Mattermost Anda.
</p>
</div>
</div>
<div className="flex flex-row items-start space-x-3 space-y-0 rounded-md border border-slate-200 p-4 dark:border-slate-800">
<input
type="checkbox"
id="mmActive"
checked={mmActive}
onChange={(e) => setMmActive(e.target.checked)}
className="mt-1 h-4 w-4 rounded border-slate-300 text-[#1B2CC1] focus:ring-[#1B2CC1]"
/>
<div className="space-y-1 leading-none">
<Label htmlFor="mmActive" className="text-sm font-bold">
Aktifkan Notifikasi
</Label>
<p className="text-[11px] text-slate-500">
Kirim notifikasi setiap kali ada yang merubah pesanannya di PO Anda.
</p>
</div>
</div>
<div className="space-y-1.5">
<Label htmlFor="mm-channel" className="text-xs font-bold tracking-wider text-slate-700 uppercase dark:text-slate-300">
Username / Channel ID
</Label>
<Input
id="mm-channel"
value={mmChannelId}
onChange={(e) => setMmChannelId(e.target.value)}
placeholder="Contoh: @firman atau 9dpxnitm..."
className="h-11 rounded-xl"
/>
</div>
</div>
)}
{profileError && (
<div className="rounded-xl bg-red-50 p-3 text-xs font-semibold text-red-600">
{profileError}
@@ -223,7 +300,7 @@ export default function ProfilePage() {
{profileSuccess && (
<div className="flex items-center gap-2 rounded-xl bg-emerald-50 p-3 text-xs font-bold text-emerald-700">
<CheckCircle className="h-4 w-4" />
<span>Profil berhasil diperbarui!</span>
<span>Perubahan berhasil disimpan!</span>
</div>
)}
</CardContent>
@@ -244,6 +321,7 @@ export default function ProfilePage() {
</div>
</form>
</Card>
</div>
{/* Right 1 Col: Account Details */}