Files
AutonetSellCar/frontend/src/app/admin/notifications/page.tsx
AutonetSellCar Deploy 1f0dcb1ddb Initial commit: AutonetSellCar platform with deployment system
- Frontend: Next.js 14 with TypeScript
- Backend: FastAPI with SQLAlchemy
- Agent: Carmodoo sync agent
- Deployment: Docker Compose based staging/production setup
- Scripts: Automated deployment with rollback support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 13:24:39 +09:00

200 lines
7.3 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useTranslation } from '@/lib/i18n';
import { notificationApi } from '@/lib/api';
export default function AdminNotificationsPage() {
const { t } = useTranslation();
// Form state
const [title, setTitle] = useState('');
const [message, setMessage] = useState('');
const [link, setLink] = useState('');
const [sending, setSending] = useState(false);
const [result, setResult] = useState<{ success: boolean; message: string } | null>(null);
const handleSendToAll = async (e: React.FormEvent) => {
e.preventDefault();
if (!title.trim() || !message.trim()) {
setResult({ success: false, message: '제목과 내용을 입력해주세요.' });
return;
}
setSending(true);
setResult(null);
try {
const response = await notificationApi.adminSendToAll(
title.trim(),
message.trim(),
link.trim() || undefined
);
setResult({ success: true, message: response.message });
setTitle('');
setMessage('');
setLink('');
} catch (error) {
console.error('Failed to send notification:', error);
setResult({ success: false, message: '알림 발송에 실패했습니다.' });
} finally {
setSending(false);
}
};
return (
<div className="space-y-6">
{/* Header */}
<div>
<h1 className="text-2xl font-bold text-gray-800"> </h1>
<p className="text-gray-600 mt-1"> .</p>
</div>
{/* Send Notification Form */}
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-lg font-semibold text-gray-800 mb-4"> </h2>
<form onSubmit={handleSendToAll} className="space-y-4">
{/* Title */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<span className="text-red-500">*</span>
</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
placeholder="알림 제목"
maxLength={200}
disabled={sending}
/>
</div>
{/* Message */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<span className="text-red-500">*</span>
</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
placeholder="알림 내용"
rows={4}
disabled={sending}
/>
</div>
{/* Link (optional) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
()
</label>
<input
type="text"
value={link}
onChange={(e) => setLink(e.target.value)}
className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
placeholder="/page-path (클릭 시 이동할 페이지)"
disabled={sending}
/>
<p className="mt-1 text-xs text-gray-500">
: /my-request, /charge, /cost
</p>
</div>
{/* Result Message */}
{result && (
<div className={`p-4 rounded-lg ${result.success ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'}`}>
{result.message}
</div>
)}
{/* Submit Button */}
<button
type="submit"
disabled={sending}
className="w-full bg-primary-600 text-white py-3 rounded-lg hover:bg-primary-700 transition disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
>
{sending ? (
<>
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
...
</>
) : (
<>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
</>
)}
</button>
</form>
</div>
{/* Notification Types Info */}
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-lg font-semibold text-gray-800 mb-4"> </h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<span className="text-2xl">🚗</span>
<div>
<p className="font-medium"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</div>
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<span className="text-2xl">🚚</span>
<div>
<p className="font-medium"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</div>
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<span className="text-2xl">💰</span>
<div>
<p className="font-medium"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</div>
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<span className="text-2xl">🎁</span>
<div>
<p className="font-medium"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</div>
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<span className="text-2xl"></span>
<div>
<p className="font-medium"> </p>
<p className="text-xs text-gray-500"> / </p>
</div>
</div>
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<span className="text-2xl">🎉</span>
<div>
<p className="font-medium"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</div>
</div>
<p className="mt-4 text-sm text-gray-600">
.
.
</p>
</div>
</div>
);
}