Refactor SMS sending to use KavehNegar VerifyLookup templates
- Removed SmsTextResolver dependency from multiple services and controllers. - Introduced dispatchTemplate method in SmsService to handle SMS sending with templates. - Updated existing SMS sending logic across various services (OtpService, PreRegistrationController, ClinicInvitationService, PaymentManager, SecretaryController, RepresentationActionController) to utilize the new dispatchTemplate method. - Enhanced SmsMessageTemplate entity to include kavenegar_template and token_map fields. - Created migration to add new fields to the sms_message_templates table and populate them with existing data. - Updated SeedSmsMessageTemplatesCommand to handle new template structure. - Added documentation for the new SMS template structure and usage.
This commit is contained in:
@@ -60,6 +60,7 @@ export default function SmsPage() {
|
||||
const [viewLog, setViewLog] = useState<SmsLog | null>(null);
|
||||
const [editMsg, setEditMsg] = useState<SmsMessageText | null>(null);
|
||||
const [editBody, setEditBody] = useState('');
|
||||
const [editKaveName, setEditKaveName] = useState('');
|
||||
const [editTemplate, setEditTemplate] = useState<SmsTemplate | null>(null);
|
||||
const [editTplName, setEditTplName] = useState('');
|
||||
const [editTplBody, setEditTplBody] = useState('');
|
||||
@@ -98,8 +99,8 @@ export default function SmsPage() {
|
||||
});
|
||||
|
||||
const updateMessageMut = useMutation({
|
||||
mutationFn: ({ tag, body }: { tag: string; body: string }) =>
|
||||
api.patch<ApiResponse<SmsMessageText>>(`/api/v1/admin/sms/messages/${tag}`, { body }),
|
||||
mutationFn: ({ tag, body, kavenegarTemplate }: { tag: string; body: string; kavenegarTemplate: string }) =>
|
||||
api.patch<ApiResponse<SmsMessageText>>(`/api/v1/admin/sms/messages/${tag}`, { body, kavenegar_template: kavenegarTemplate }),
|
||||
onSuccess: () => {
|
||||
toast.success('متن پیامک بهروزرسانی شد');
|
||||
setEditMsg(null);
|
||||
@@ -485,11 +486,17 @@ export default function SmsPage() {
|
||||
<div key={m.tag} className="card card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<b style={{ fontSize: 14 }}>{m.title}</b>
|
||||
<button className="btn ghost sm" onClick={() => { setEditMsg(m); setEditBody(m.body); }}>
|
||||
<button className="btn ghost sm" onClick={() => { setEditMsg(m); setEditBody(m.body); setEditKaveName(m.kavenegar_template ?? ''); }}>
|
||||
<PencilIcon style={{ width: 14, height: 14 }} /> ویرایش
|
||||
</button>
|
||||
</div>
|
||||
<div className="muted" style={{ fontSize: 13, lineHeight: 1.9, whiteSpace: 'pre-wrap', direction: 'rtl' }}>{m.body}</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span className="muted" style={{ fontSize: 12 }}>تمپلت کاوهنگار:</span>
|
||||
<span className={`badge ${m.kavenegar_template ? 'green' : 'gray'}`} dir="ltr" style={{ fontSize: 11 }}>
|
||||
{m.kavenegar_template || 'تعریفنشده'}
|
||||
</span>
|
||||
</div>
|
||||
{m.variables.length > 0 && (
|
||||
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
||||
{m.variables.map((v) => <span key={v} className="chip" dir="ltr">{`{${v}}`}</span>)}
|
||||
@@ -513,7 +520,7 @@ export default function SmsPage() {
|
||||
<button
|
||||
className="btn primary sm"
|
||||
disabled={updateMessageMut.isPending || !editBody.trim()}
|
||||
onClick={() => editMsg && updateMessageMut.mutate({ tag: editMsg.tag, body: editBody })}
|
||||
onClick={() => editMsg && updateMessageMut.mutate({ tag: editMsg.tag, body: editBody, kavenegarTemplate: editKaveName })}
|
||||
>
|
||||
{updateMessageMut.isPending ? 'در حال ذخیره...' : 'ذخیره'}
|
||||
</button>
|
||||
@@ -521,13 +528,22 @@ export default function SmsPage() {
|
||||
}
|
||||
>
|
||||
<div className="form-row">
|
||||
<label>متن پیامک</label>
|
||||
<label>متن پیامک (پیشنمایش/لاگ)</label>
|
||||
<textarea className="input" rows={5} dir="rtl" value={editBody} onChange={(e) => setEditBody(e.target.value)} />
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label>نام تمپلت کاوهنگار (VerifyLookup)</label>
|
||||
<input className="input" dir="ltr" value={editKaveName} onChange={(e) => setEditKaveName(e.target.value)} placeholder="clinicpro-otp" />
|
||||
<span className="muted" style={{ fontSize: 11, marginTop: 4 }}>
|
||||
این نام باید دقیقاً با تمپلت تأییدشده در پنل کاوهنگار یکی باشد. متن واقعی پیامک از پنل کاوهنگار ارسال میشود، نه از این متن.
|
||||
</span>
|
||||
</div>
|
||||
{editMsg && editMsg.variables.length > 0 && (
|
||||
<div style={{ marginTop: 10, display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<span className="muted" style={{ fontSize: 12 }}>متغیرهای مجاز:</span>
|
||||
{editMsg.variables.map((v) => <span key={v} className="chip" dir="ltr">{`{${v}}`}</span>)}
|
||||
<span className="muted" style={{ fontSize: 12 }}>متغیرها → token:</span>
|
||||
{editMsg.variables.map((v) => (
|
||||
<span key={v} className="chip" dir="ltr">{`{${v}}`}{editMsg.token_map?.[v] ? ` → %${editMsg.token_map[v]}` : ''}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
@@ -220,6 +220,8 @@ export interface SmsMessageText {
|
||||
title: string;
|
||||
body: string;
|
||||
variables: string[];
|
||||
kavenegar_template: string | null;
|
||||
token_map: Record<string, string>;
|
||||
updated_at: number | null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user