[fix] negodata·frontend·backend: 회사별 브랜드 색(primary_color) 기능 전면 제거 — 편집 UI 없이 값만 남아 적용되던 구현 정리(포털·메일·사이드바 색 솔루션 고정, 시드·응답 필드 제거)
This commit is contained in:
parent
4ed23c56b8
commit
11b29b45e3
@ -72,5 +72,4 @@ class Res_HidePopup(Res_WebPacketProtocol):
|
|||||||
class Res_SessionBranding(Res_WebPacketProtocol):
|
class Res_SessionBranding(Res_WebPacketProtocol):
|
||||||
service_name: str = Field("", description="회사 서비스명(companies.settings.branding.service_name). 미설정 시 빈 값")
|
service_name: str = Field("", description="회사 서비스명(companies.settings.branding.service_name). 미설정 시 빈 값")
|
||||||
logo_url: str = Field("", description="회사 로고 URL")
|
logo_url: str = Field("", description="회사 로고 URL")
|
||||||
primary_color: str = Field("", description="브랜드 색상(hex)")
|
|
||||||
helpdesk: list = Field(default_factory=list, description="헬프데스크 연락처 줄 목록(companies.settings.branding.helpdesk). 한 줄 = 담당자 한 명")
|
helpdesk: list = Field(default_factory=list, description="헬프데스크 연락처 줄 목록(companies.settings.branding.helpdesk). 한 줄 = 담당자 한 명")
|
||||||
|
|||||||
@ -279,7 +279,6 @@ class AuthService:
|
|||||||
branding = branding or {}
|
branding = branding or {}
|
||||||
res.service_name = branding.get("service_name") or ""
|
res.service_name = branding.get("service_name") or ""
|
||||||
res.logo_url = branding.get("logo_url") or ""
|
res.logo_url = branding.get("logo_url") or ""
|
||||||
res.primary_color = branding.get("primary_color") or ""
|
|
||||||
res.helpdesk = branding.get("helpdesk") or []
|
res.helpdesk = branding.get("helpdesk") or []
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,6 @@ export interface RefreshTokenResponse {
|
|||||||
export interface Branding {
|
export interface Branding {
|
||||||
service_name?: string
|
service_name?: string
|
||||||
logo_url?: string
|
logo_url?: string
|
||||||
primary_color?: string
|
|
||||||
helpdesk?: string[] // 헬프데스크 연락처 — 한 줄 = 담당자 한 명. 비면 연락처 영역을 렌더하지 않는다
|
helpdesk?: string[] // 헬프데스크 연락처 — 한 줄 = 담당자 한 명. 비면 연락처 영역을 렌더하지 않는다
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +65,6 @@ export interface SessionBrandingResponse {
|
|||||||
result: ApiResult
|
result: ApiResult
|
||||||
service_name: string
|
service_name: string
|
||||||
logo_url: string
|
logo_url: string
|
||||||
primary_color: string
|
|
||||||
helpdesk?: string[]
|
helpdesk?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,10 +43,9 @@ export function usePreLoginBranding(): Branding | null {
|
|||||||
const next: Branding = {
|
const next: Branding = {
|
||||||
service_name: res.service_name || undefined,
|
service_name: res.service_name || undefined,
|
||||||
logo_url: res.logo_url || undefined,
|
logo_url: res.logo_url || undefined,
|
||||||
primary_color: res.primary_color || undefined,
|
|
||||||
helpdesk: res.helpdesk?.length ? res.helpdesk : undefined,
|
helpdesk: res.helpdesk?.length ? res.helpdesk : undefined,
|
||||||
}
|
}
|
||||||
if (!next.service_name && !next.logo_url && !next.primary_color && !next.helpdesk) return
|
if (!next.service_name && !next.logo_url && !next.helpdesk) return
|
||||||
setBranding(next)
|
setBranding(next)
|
||||||
writeCached(next) // 다음 진입에 session_id 가 없어도 이 회사로 보이게 한다
|
writeCached(next) // 다음 진입에 session_id 가 없어도 이 회사로 보이게 한다
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { type ReactNode } from 'react'
|
import { type ReactNode } from 'react'
|
||||||
import { Logo } from '@/components'
|
import { Logo } from '@/components'
|
||||||
import { useMeQuery } from '@/apis'
|
import { useMeQuery } from '@/apis'
|
||||||
import { cn, useBrandColor } from '@/lib'
|
import { cn } from '@/lib'
|
||||||
|
|
||||||
// 좌측 폭: list=반응형 비율 / chat=고정폭 단계 축소
|
// 좌측 폭: list=반응형 비율 / chat=고정폭 단계 축소
|
||||||
const SIDEBAR_WIDTH = {
|
const SIDEBAR_WIDTH = {
|
||||||
@ -44,8 +44,7 @@ export function MainLayout({
|
|||||||
header,
|
header,
|
||||||
children,
|
children,
|
||||||
}: MainLayoutProps) {
|
}: MainLayoutProps) {
|
||||||
const { data: user } = useMeQuery() // 회사 브랜딩(서비스명/로고/브랜드 색) 주입
|
const { data: user } = useMeQuery() // 회사 브랜딩(서비스명/로고) 주입 — 색은 솔루션 고정
|
||||||
useBrandColor(user?.branding?.primary_color)
|
|
||||||
const panes = (
|
const panes = (
|
||||||
<>
|
<>
|
||||||
<aside className={cn(styles.sidebar, SIDEBAR_WIDTH[sidebarWidth])}>
|
<aside className={cn(styles.sidebar, SIDEBAR_WIDTH[sidebarWidth])}>
|
||||||
|
|||||||
@ -4,4 +4,3 @@ export type { ClassValue } from '@/lib/cn'
|
|||||||
export { interactive } from '@/lib/interactive'
|
export { interactive } from '@/lib/interactive'
|
||||||
export { toast } from '@/lib/toast'
|
export { toast } from '@/lib/toast'
|
||||||
export { formatKstDateTime, KST_TIME_ZONE } from '@/lib/datetime'
|
export { formatKstDateTime, KST_TIME_ZONE } from '@/lib/datetime'
|
||||||
export { useBrandColor } from '@/lib/useBrandColor'
|
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
import { useEffect } from 'react'
|
|
||||||
|
|
||||||
// 회사 브랜드 색(companies.settings.branding.primary_color)을 브랜드 CSS 변수에 적용한다.
|
|
||||||
// hover/ring/연한 배경은 color-mix 파생으로 만들어 토스풍 톤 관계를 유지하고,
|
|
||||||
// 색이 없거나 hex 가 아니면(injection 방지) 기본 팔레트(Toss blue)를 그대로 둔다.
|
|
||||||
const HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/
|
|
||||||
|
|
||||||
const DERIVED = {
|
|
||||||
'--brand-600': (c: string) => c,
|
|
||||||
'--brand-700': (c: string) => `color-mix(in srgb, ${c} 85%, #000)`,
|
|
||||||
'--brand-500': (c: string) => `color-mix(in srgb, ${c} 70%, #fff)`,
|
|
||||||
'--brand-light': (c: string) => `color-mix(in srgb, ${c} 9%, #fff)`,
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export function useBrandColor(color?: string) {
|
|
||||||
useEffect(() => {
|
|
||||||
const c = (color ?? '').trim()
|
|
||||||
if (!HEX_COLOR.test(c)) return
|
|
||||||
const root = document.documentElement.style
|
|
||||||
for (const [name, derive] of Object.entries(DERIVED)) {
|
|
||||||
root.setProperty(name, derive(c))
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
for (const name of Object.keys(DERIVED)) {
|
|
||||||
root.removeProperty(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [color])
|
|
||||||
}
|
|
||||||
@ -2,12 +2,10 @@ import { Navigate } from 'react-router'
|
|||||||
import { tokenStorage } from '@/apis'
|
import { tokenStorage } from '@/apis'
|
||||||
import { Logo } from '@/components'
|
import { Logo } from '@/components'
|
||||||
import { LoginForm, usePreLoginBranding } from '@/features/auth'
|
import { LoginForm, usePreLoginBranding } from '@/features/auth'
|
||||||
import { useBrandColor } from '@/lib'
|
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
// 초청 링크의 session_id(없으면 직전 로그인 캐시)로 회사 브랜딩을 먼저 그린다.
|
// 초청 링크의 session_id(없으면 직전 로그인 캐시)로 회사 브랜딩(서비스명·로고)을 먼저 그린다.
|
||||||
const branding = usePreLoginBranding()
|
const branding = usePreLoginBranding()
|
||||||
useBrandColor(branding?.primary_color)
|
|
||||||
|
|
||||||
// 이미 로그인된 상태면 목록으로
|
// 이미 로그인된 상태면 목록으로
|
||||||
if (tokenStorage.hasToken()) {
|
if (tokenStorage.hasToken()) {
|
||||||
|
|||||||
@ -28,8 +28,6 @@ _DEFAULT_EMAIL_COLOR = "#3182f6"
|
|||||||
_DEFAULT_EMAIL_HEADER = "NEGODATA"
|
_DEFAULT_EMAIL_HEADER = "NEGODATA"
|
||||||
_DEFAULT_EMAIL_GREETING = "아래 견적 건의 협상에 참여해 주세요."
|
_DEFAULT_EMAIL_GREETING = "아래 견적 건의 협상에 참여해 주세요."
|
||||||
|
|
||||||
_HEX_COLOR = re.compile(r"^#[0-9a-fA-F]{3,8}$")
|
|
||||||
|
|
||||||
# HTML 본문은 코드에 박지 않고 파일에서 읽는다(모듈 로드 시 1회). $placeholder 는 string.Template 로 치환.
|
# HTML 본문은 코드에 박지 않고 파일에서 읽는다(모듈 로드 시 1회). $placeholder 는 string.Template 로 치환.
|
||||||
_TEMPLATE_DIR = Path(__file__).parent / "email_templates"
|
_TEMPLATE_DIR = Path(__file__).parent / "email_templates"
|
||||||
_INVITE_HTML = Template((_TEMPLATE_DIR / "invite_email.html").read_text(encoding="utf-8"))
|
_INVITE_HTML = Template((_TEMPLATE_DIR / "invite_email.html").read_text(encoding="utf-8"))
|
||||||
@ -112,10 +110,10 @@ def build_invite_email(
|
|||||||
|
|
||||||
목표가·앵커링가는 협상 전략 값이라 메일에 담지 않는다(공급사에게 노출 금지).
|
목표가·앵커링가는 협상 전략 값이라 메일에 담지 않는다(공급사에게 노출 금지).
|
||||||
공급사는 링크로 협상 화면에 진입해 입찰한다.
|
공급사는 링크로 협상 화면에 진입해 입찰한다.
|
||||||
branding = companies.settings.branding — 헤더·색은 회사 CI(logo_url·service_name·primary_color)를
|
branding = companies.settings.branding — 헤더는 회사 CI(logo_url·service_name)를
|
||||||
그대로 쓴다(메일 전용 이중 설정 금지). 메일 전용 키는 인사 문구(email_greeting) 하나뿐.
|
그대로 쓴다(메일 전용 이중 설정 금지). 메일 전용 키는 인사 문구(email_greeting) 하나뿐.
|
||||||
헤더는 로고 있으면 로고, 없으면 service_name → 회사명(company_name) → NEGODATA 텍스트 —
|
헤더는 로고 있으면 로고, 없으면 service_name → 회사명(company_name) → NEGODATA 텍스트 —
|
||||||
공급사에겐 솔루션명보다 발주사가 보여야 한다. 색은 primary_color → 기본(챗 포털 브랜드 블루) 순.
|
공급사에겐 솔루션명보다 발주사가 보여야 한다. 색은 회사별 커스텀 없이 솔루션 기본색 고정.
|
||||||
레이아웃은 고정이고 값만 갈아끼우므로, 어떤 값을 넣어도 메일이 깨지지 않는다.
|
레이아웃은 고정이고 값만 갈아끼우므로, 어떤 값을 넣어도 메일이 깨지지 않는다.
|
||||||
"""
|
"""
|
||||||
b = branding or {}
|
b = branding or {}
|
||||||
@ -133,7 +131,7 @@ def build_invite_email(
|
|||||||
qt_number=escape(qt_number),
|
qt_number=escape(qt_number),
|
||||||
deadline=escape(deadline),
|
deadline=escape(deadline),
|
||||||
chat_url=escape(chat_url),
|
chat_url=escape(chat_url),
|
||||||
brand_color=_safe_color(b.get("primary_color")),
|
brand_color=_DEFAULT_EMAIL_COLOR,
|
||||||
header_content=_header_content(header_name, (b.get("logo_url") or "").strip()),
|
header_content=_header_content(header_name, (b.get("logo_url") or "").strip()),
|
||||||
email_greeting=escape(greeting),
|
email_greeting=escape(greeting),
|
||||||
)
|
)
|
||||||
@ -149,12 +147,6 @@ def build_invite_email(
|
|||||||
return subject, html, text
|
return subject, html, text
|
||||||
|
|
||||||
|
|
||||||
def _safe_color(value: str | None) -> str:
|
|
||||||
"""브랜드 색은 hex 만 통과 — 이상값이 style 속성을 깨거나 CSS 를 주입하지 못하게 기본색으로 되돌린다."""
|
|
||||||
v = (value or "").strip()
|
|
||||||
return v if _HEX_COLOR.match(v) else _DEFAULT_EMAIL_COLOR
|
|
||||||
|
|
||||||
|
|
||||||
def _header_content(name: str, logo_url: str) -> str:
|
def _header_content(name: str, logo_url: str) -> str:
|
||||||
"""헤더(흰 배경) 내용물 — 회사 CI 로고가 있으면 이미지(회사명은 alt 로), 없으면 회사명 텍스트. escape 는 여기서 끝낸다."""
|
"""헤더(흰 배경) 내용물 — 회사 CI 로고가 있으면 이미지(회사명은 alt 로), 없으면 회사명 텍스트. escape 는 여기서 끝낸다."""
|
||||||
if logo_url:
|
if logo_url:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"""협상 초청 메일 미리보기(/v1/company/settings/email-preview) 테스트 — 회사 CI 슬롯 렌더 확인.
|
"""협상 초청 메일 미리보기(/v1/company/settings/email-preview) 테스트 — 회사 CI 슬롯 렌더 확인.
|
||||||
|
|
||||||
- 실제 발송(services/quotation/invites.py)과 같은 build_invite_email 을 타므로 미리보기 검증 = 발송물 검증.
|
- 실제 발송(services/quotation/invites.py)과 같은 build_invite_email 을 타므로 미리보기 검증 = 발송물 검증.
|
||||||
- 헤더·색은 회사 CI(logo_url·service_name·primary_color)를 그대로 쓴다 — 메일 전용 설정은 인사 문구뿐.
|
- 헤더는 회사 CI(logo_url·service_name)를 그대로 쓰고 색은 솔루션 고정 — 메일 전용 설정은 인사 문구뿐.
|
||||||
- 색은 hex 만 통과한다(이상값이 style 을 깨거나 CSS 주입되는 것 방지).
|
- 색은 hex 만 통과한다(이상값이 style 을 깨거나 CSS 주입되는 것 방지).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -32,13 +32,13 @@ async def test_preview_default_branding(client, auth_headers):
|
|||||||
|
|
||||||
|
|
||||||
async def test_preview_ci_branding(client, auth_headers):
|
async def test_preview_ci_branding(client, auth_headers):
|
||||||
"""검증: 회사 CI(서비스명·로고·브랜드 색)와 인사 문구를 채워 미리보기 호출.
|
"""검증: 회사 CI(서비스명·로고)와 인사 문구를 채워 미리보기 호출(브랜드 색을 넣어도 무시).
|
||||||
기대결과: 헤더는 로고 이미지(서비스명은 alt), 액센트·버튼은 브랜드 색으로 렌더되고 기본색은 사라진다."""
|
기대결과: 헤더는 로고 이미지(서비스명은 alt), 색은 회사 값과 무관하게 솔루션 기본색으로 렌더."""
|
||||||
h = await auth_headers("previewer2")
|
h = await auth_headers("previewer2")
|
||||||
branding = {
|
branding = {
|
||||||
"service_name": "아이좋아네고",
|
"service_name": "아이좋아네고",
|
||||||
"logo_url": "https://cdn.example.com/ci.png",
|
"logo_url": "https://cdn.example.com/ci.png",
|
||||||
"primary_color": "#f551a0",
|
"primary_color": "#f551a0", # 제거된 기능 — 값을 보내도 반영되지 않아야 한다
|
||||||
"email_greeting": "협상에 초대합니다.",
|
"email_greeting": "협상에 초대합니다.",
|
||||||
}
|
}
|
||||||
r = await client.post(PREVIEW_URL, json={"branding": branding}, headers=h)
|
r = await client.post(PREVIEW_URL, json={"branding": branding}, headers=h)
|
||||||
@ -46,8 +46,8 @@ async def test_preview_ci_branding(client, auth_headers):
|
|||||||
html = r.json()["html"]
|
html = r.json()["html"]
|
||||||
assert '<img src="https://cdn.example.com/ci.png"' in html
|
assert '<img src="https://cdn.example.com/ci.png"' in html
|
||||||
assert 'alt="아이좋아네고"' in html
|
assert 'alt="아이좋아네고"' in html
|
||||||
assert "#f551a0" in html
|
assert "#f551a0" not in html
|
||||||
assert "#3182f6" not in html
|
assert "#3182f6" in html
|
||||||
assert "협상에 초대합니다." in html
|
assert "협상에 초대합니다." in html
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -180,11 +180,7 @@ export default function Layout({ children, currentPage, setPage, onLogout }: Lay
|
|||||||
{branding.logoUrl ? (
|
{branding.logoUrl ? (
|
||||||
<img src={branding.logoUrl} alt={branding.serviceName} className="h-4 max-w-24 object-contain" />
|
<img src={branding.logoUrl} alt={branding.serviceName} className="h-4 max-w-24 object-contain" />
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span aria-hidden className="size-4 rounded-[5px] bg-primary" />
|
||||||
aria-hidden
|
|
||||||
className="size-4 rounded-[5px] bg-primary"
|
|
||||||
style={branding.primaryColor ? { backgroundColor: branding.primaryColor } : undefined}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{branding.serviceName}
|
{branding.serviceName}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@ -56,7 +56,6 @@ const SETTINGS_TAB_ICON: Record<SettingsTab, ElementType> = {
|
|||||||
type BrandingTextKey =
|
type BrandingTextKey =
|
||||||
| 'service_name'
|
| 'service_name'
|
||||||
| 'logo_url'
|
| 'logo_url'
|
||||||
| 'primary_color'
|
|
||||||
| 'email_greeting';
|
| 'email_greeting';
|
||||||
|
|
||||||
export function SettingsView({ tabs = SETTINGS_TABS }: { tabs?: readonly SettingsTab[] }) {
|
export function SettingsView({ tabs = SETTINGS_TABS }: { tabs?: readonly SettingsTab[] }) {
|
||||||
@ -240,11 +239,7 @@ export function SettingsView({ tabs = SETTINGS_TABS }: { tabs?: readonly Setting
|
|||||||
{draft.branding?.logo_url ? (
|
{draft.branding?.logo_url ? (
|
||||||
<img src={draft.branding.logo_url} alt="로고 미리보기" className="h-4 max-w-24 object-contain" />
|
<img src={draft.branding.logo_url} alt="로고 미리보기" className="h-4 max-w-24 object-contain" />
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span aria-hidden className="size-4 rounded-[5px] bg-primary" />
|
||||||
aria-hidden
|
|
||||||
className="size-4 rounded-[5px]"
|
|
||||||
style={{ backgroundColor: draft.branding?.primary_color || 'var(--primary)' }}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{draft.branding?.service_name || 'NegoData'}
|
{draft.branding?.service_name || 'NegoData'}
|
||||||
</div>
|
</div>
|
||||||
@ -676,11 +671,10 @@ function Field({ label, hint, children }: { label: string; hint?: string; childr
|
|||||||
// 초청 메일 미리보기 — 실제 발송과 같은 백엔드 템플릿을 샘플 견적으로 렌더해 iframe 에 띄운다.
|
// 초청 메일 미리보기 — 실제 발송과 같은 백엔드 템플릿을 샘플 견적으로 렌더해 iframe 에 띄운다.
|
||||||
// 입력 중 매 타자마다 서버를 부르지 않도록 디바운스하고, 갱신 사이엔 직전 렌더를 유지해 깜빡임을 없앤다.
|
// 입력 중 매 타자마다 서버를 부르지 않도록 디바운스하고, 갱신 사이엔 직전 렌더를 유지해 깜빡임을 없앤다.
|
||||||
function InviteEmailPreview({ branding }: { branding?: CompanySettings['branding'] }) {
|
function InviteEmailPreview({ branding }: { branding?: CompanySettings['branding'] }) {
|
||||||
// 메일 헤더·색은 회사 CI(로고·서비스명·브랜드 색)를 그대로 쓰므로 CI 편집값을 같이 보낸다(미리보기 = 실발송).
|
// 메일 헤더는 회사 CI(로고·서비스명)를 그대로 쓰므로 CI 편집값을 같이 보낸다(미리보기 = 실발송). 색은 솔루션 고정.
|
||||||
const emailBranding = JSON.stringify({
|
const emailBranding = JSON.stringify({
|
||||||
service_name: branding?.service_name ?? '',
|
service_name: branding?.service_name ?? '',
|
||||||
logo_url: branding?.logo_url ?? '',
|
logo_url: branding?.logo_url ?? '',
|
||||||
primary_color: branding?.primary_color ?? '',
|
|
||||||
email_greeting: branding?.email_greeting ?? '',
|
email_greeting: branding?.email_greeting ?? '',
|
||||||
});
|
});
|
||||||
const debounced = useDebounced(emailBranding, 400);
|
const debounced = useDebounced(emailBranding, 400);
|
||||||
|
|||||||
@ -18,8 +18,7 @@ export type CompanySettings = {
|
|||||||
branding?: {
|
branding?: {
|
||||||
service_name?: string; // 사이드바/타이틀 서비스명 (기본 NegoData)
|
service_name?: string; // 사이드바/타이틀 서비스명 (기본 NegoData)
|
||||||
logo_url?: string; // 로고 이미지 URL. 없으면 색상 사각형+텍스트
|
logo_url?: string; // 로고 이미지 URL. 없으면 색상 사각형+텍스트
|
||||||
primary_color?: string; // 브랜드 색 (hex) — 공급사 포털·초청 메일 포인트 색 (기본 #3182f6)
|
email_greeting?: string; // 초청 메일 인사 문구 ('OO 담당자님,' 다음 줄). 메일 헤더는 CI(logo_url·service_name)를 따르고 색은 솔루션 고정
|
||||||
email_greeting?: string; // 초청 메일 인사 문구 ('OO 담당자님,' 다음 줄). 메일 헤더·색은 CI(logo_url·service_name·primary_color)를 따른다
|
|
||||||
helpdesk?: string[]; // 헬프데스크 연락처 — 한 줄 = 담당자 한 명. 공급사 포털 3곳(로그인·메뉴·안내팝업)이 그대로 출력
|
helpdesk?: string[]; // 헬프데스크 연락처 — 한 줄 = 담당자 한 명. 공급사 포털 3곳(로그인·메뉴·안내팝업)이 그대로 출력
|
||||||
};
|
};
|
||||||
labels?: Record<string, string>; // 카탈로그 키 → 이 회사 용어 (없으면 기본값)
|
labels?: Record<string, string>; // 카탈로그 키 → 이 회사 용어 (없으면 기본값)
|
||||||
|
|||||||
@ -41,12 +41,11 @@ export function useVatMode(): VatMode {
|
|||||||
return settings.features?.vat_mode ?? DEFAULT_VAT_MODE;
|
return settings.features?.vat_mode ?? DEFAULT_VAT_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 브랜딩 헬퍼. 서비스명·로고 — 미설정 시 기본 브랜드(NegoData).
|
// 브랜딩 헬퍼. 서비스명·로고 — 미설정 시 기본 브랜드(NegoData). 색은 회사별 커스텀 없이 솔루션 고정.
|
||||||
export function useBranding() {
|
export function useBranding() {
|
||||||
const { settings } = useCompanySettings();
|
const { settings } = useCompanySettings();
|
||||||
return {
|
return {
|
||||||
serviceName: settings.branding?.service_name || 'NegoData',
|
serviceName: settings.branding?.service_name || 'NegoData',
|
||||||
logoUrl: settings.branding?.logo_url || null,
|
logoUrl: settings.branding?.logo_url || null,
|
||||||
primaryColor: settings.branding?.primary_color || null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,8 +12,7 @@
|
|||||||
},
|
},
|
||||||
"branding": {
|
"branding": {
|
||||||
"logo_url": "https://ado2mediastoragepublic.blob.core.windows.net/ado2-media-public-access/negodata/a35152d2-db61-4760-9f1e-beb9736d957f/items/4ab23e35e4f14d888591ca0f6d343fc8.jpg",
|
"logo_url": "https://ado2mediastoragepublic.blob.core.windows.net/ado2-media-public-access/negodata/a35152d2-db61-4760-9f1e-beb9736d957f/items/4ab23e35e4f14d888591ca0f6d343fc8.jpg",
|
||||||
"service_name": "아이좋아네고",
|
"service_name": "아이좋아네고"
|
||||||
"primary_color": "#f551a0"
|
|
||||||
},
|
},
|
||||||
"item_fields": [
|
"item_fields": [
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user