fix(push): 일괄 판매기간 적용도 알림 발송 + 알림 아이콘 모모 로고/단색 배지
Deploy momo-erp / deploy (push) Successful in 1m56s

- bulk-sale-range: 리스트에서 판매기간 일괄 적용 시에도 일반 사용자 푸시.
  1건이면 품목명, 여러 건이면 'N개 품목 판매' 요약. 해제(clear)는 알림 제외.
- 알림 아이콘: 큰 아이콘은 모모 로고(icon-192), 상태바 작은 배지는 흰 M
  단색 투명 PNG(badge-96) — 기존엔 컬러 PNG라 크롬이 지구본 기본 배지로 대체했음.
- sw.js: CACHE v2 로 올려 갱신 강제 + badge-96 precache, push 핸들러가
  payload icon/badge 우선 사용.
This commit is contained in:
chpark
2026-05-27 00:43:45 +09:00
parent 21c8bf5ab5
commit 89503ebf03
3 changed files with 49 additions and 4 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1014 B

+4 -4
View File
@@ -1,6 +1,6 @@
// 모모유통 ERP — Service Worker (PWA install criteria 충족용)
const CACHE = 'momo-erp-v1';
const PRECACHE = ['/', '/manifest.json', '/icon-192.png', '/icon-512.png'];
const CACHE = 'momo-erp-v2';
const PRECACHE = ['/', '/manifest.json', '/icon-192.png', '/icon-512.png', '/badge-96.png'];
self.addEventListener('install', (e) => {
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(PRECACHE)).catch(() => {}));
@@ -33,8 +33,8 @@ self.addEventListener('push', (e) => {
const title = data.title || '모모유통';
const options = {
body: data.body || '',
icon: '/icon-192.png',
badge: '/icon-192.png',
icon: data.icon || '/icon-192.png', // 큰 아이콘 = 모모 로고(초록 M)
badge: data.badge || '/badge-96.png', // 상태바 작은 아이콘 = 흰 M 단색(투명 배경)
tag: data.tag || undefined,
data: { url: data.url || '/m/orders/new' },
};
@@ -2,6 +2,7 @@
import { NextRequest, NextResponse } from "next/server";
import { pool } from "@/lib/db";
import { requireMomoAdmin } from "@/lib/momo-guard";
import { sendPush } from "@/lib/push";
export async function POST(req: NextRequest) {
const g = await requireMomoAdmin();
@@ -28,5 +29,49 @@ export async function POST(req: NextRequest) {
WHERE objid IN (${placeholders})`,
[...objids, start, end, userId]
);
// 일괄 적용한 판매 일정이 유효(오늘/미래)한 품목이 있으면 일반 사용자에게 알림.
// 판매기간 해제(clear)는 알림 대상 아님. (상세 수정과 동일 정책)
if (!clear) {
try {
const sell = await pool.query<{ item_name: string; start_txt: string | null; orderable_now: boolean }>(
`SELECT item_name,
TO_CHAR(sale_start_date,'YYYY-MM-DD HH24:MI') AS start_txt,
(sale_start_date IS NULL OR (NOW() AT TIME ZONE 'Asia/Seoul') >= sale_start_date) AS orderable_now
FROM momo_items
WHERE objid IN (${placeholders})
AND COALESCE(is_del,'N') != 'Y'
AND UPPER(COALESCE(status,'')) = 'ACTIVE'
AND COALESCE(is_hidden,'N') != 'Y'
AND (sale_start_date IS NOT NULL OR sale_end_date IS NOT NULL)
AND (
sale_end_date IS NULL
OR (NOW() AT TIME ZONE 'Asia/Seoul') <= CASE
WHEN sale_end_date = date_trunc('day', sale_end_date)
THEN sale_end_date + INTERVAL '1 day' - INTERVAL '1 second'
ELSE sale_end_date
END
)`,
objids
);
const rows = sell.rows;
if (rows.length === 1) {
const r = rows[0];
const body = r.orderable_now
? `${r.item_name} — 지금 출고요청할 수 있어요.`
: `${r.item_name}${r.start_txt ?? "곧"} 판매 예정입니다.`;
await sendPush({ title: "판매 품목 안내", body, url: "/m/orders/new", tag: "item-bulk" }, undefined, { generalOnly: true });
} else if (rows.length > 1) {
await sendPush(
{ title: "판매 품목 안내", body: `${rows.length}개 품목 판매가 시작/예정되었어요. 지금 확인해보세요.`, url: "/m/orders/new", tag: "item-bulk" },
undefined,
{ generalOnly: true }
);
}
} catch (e) {
console.error("[bulk-sale-range notify]", e);
}
}
return NextResponse.json({ success: true, count: res.rowCount ?? 0 });
}