본문 바로가기

관리자 공통 컴포넌트

마지막 갱신 

관리자(admin/) 직원 · 본사 공통 스타일. 수정 : admin/css/common.css (토큰 · 타이포 · 컴포넌트) / admin/css/layout.css (aside · header · main) / 직원 · 본사 다른 부분은 staff/css/staff.css · hq/css/hq.css. 이 페이지는 납품 전 제거.

레이아웃 · 페이지 변수

aside(300, 1440 이하 260) + 본문(header 120 + main). 1024 이하 : aside 는 왼쪽 드로어, header 에 메뉴 버튼 · 위치 표시 숨김.

<body class="admin-staff | admin-hq">
<div class="admin-wrap">
    <aside class="admin-aside">          ← 로고 · 메뉴(.admin-nav) · 확인용 링크 · 접속 정보 (sticky 화면 높이)
    <div class="admin-body">
        <header class="admin-header">    ← 위치 표시(.crumb-list) · 제목(h1.page-tit) · 마지막 갱신 · 새로고침
        <main class="admin-main">        ← 페이지 본문 (바로 아래 section 끼리 30 간격)
    </div>
</div>

페이지 상단 변수 (inc/header.php)

페이지 변수
변수설명
$root / $admin사이트 루트 / admin 폴더 상대경로 (staff/index.php → '../../' / '../')
$role'staff' 직원 / 'hq' 본사 관리자 → {role}/data/menu.php · {role}/css/{role}.css · {role}/js/{role}.js(있을 때) 를 읽는다
$pageId메뉴 활성 (menu.php 의 id). 본사는 현재 페이지가 든 그룹만 펼침
$pageTitle · $pageCrumb헤더 제목(h1) · 위치 표시. $pageCrumb 를 안 넘기면 menu.php 의 $roleCrumb
$pageCss · $pageJs페이지 전용 CSS/JS (admin 기준 경로, 예 'css/reserve.css')

메뉴 데이터 ({role}/data/menu.php → $adminMenu)

메뉴 데이터 키
키설명
tit · fold그룹 이름 · 접기 사용 (본사). 직원은 tit 없이 묶음 사이 구분선
id · name · href페이지 $pageId 와 비교 · 메뉴 이름 · {role} 폴더 기준 경로
count · tag오른쪽 숫자 뱃지(.count-badge) · 권한 뱃지(.badge.line)

타이포그래피 (STAFF TEXT STYLE GUIDE)

폰트 SUIT Variable. 모든 글자는 아래 타이포 클래스를 재활용한다. 컴포넌트 CSS 에 font-size · font-weight · line-height 를 쓰지 않고 마크업에 컴포넌트 클래스 + 타이포 클래스를 함께 쓴다 (예 : .square-btn.main-btn.btn-txt / .stat-num.hero). 값은 css/common.css 에서 읽어 표시.

  • .hero36 / 800 / 140%

    14:12:02

  • .section-tit22 / 700 / 140%

    현재 작업

  • .sub-section-tit18 / 600 / 145%

    첫 이용 후 누적 재방문

  • .card-tit16 / 600 / 150%

    2026년 10월 7일 수요일

  • .card-desc14 / 400 / 150%

    진행 중 4대 중 2대 표시

  • .body-large16 / 400 / 160%

    오늘 · 판교점 영업 09:00-19:00

  • .body-medium14 / 400 / 150%

    12가 3456 쏘나타 DN8

  • .body-small13 / 400 / 150%

    010-0000-0000

  • .btn-txt14 / 600 / 140%

    예약 등록

  • .badge-txt13 / 600 / 140%

    결제 완료

현재 작업

색 · 굵기 유틸 : .static2-txt .static4-txt .red-txt .font2-txt .font3-txt .font4-txt / .weight400 ~ .weight700

코드 보기
<h2 class="section-tit">현재 작업</h2>
<p class="body-medium font3-txt">색 · 굵기 유틸 : .static2-txt .static4-txt .red-txt .font2-txt .font3-txt .font4-txt / .weight400 ~ .weight700</p>

컬러 토큰 (COLOR STYLE GUIDE)

css/common.css :root 값을 그대로 읽어 표시. --color-font5 (#CCC) 는 흰 배경 대비 부족 → 비활성 표시에만 사용, 일반 텍스트 최소는 --color-font4.

배경 (main · section)

  • --color-bg
    #F7F8FA
  • --color-section1
    #FFFFFF
  • --color-section2
    #F3F5F7
  • --color-section3
    #EDF5F2
  • --color-section4
    #EEF3FC

Static

  • --color-static1
    #244A9D
  • --color-static2
    #3568D4
  • --color-static3
    #DCE7FC
  • --color-static4
    #2E8A57

Font

  • --color-font1
    #202020
  • --color-font2
    #484848
  • --color-font3
    #686868
  • --color-font4
    #999999
  • --color-font5
    #CCCCCC
  • --color-font6
    #FFFFFF

Border

  • --color-border1
    #EAECF0
  • --color-border2
    #D0D5DD
  • --color-border3
    #B9CDF5
  • --color-border4
    #3568D4

상태 (뱃지)

  • --color-blue
    #2855B5
  • --color-blue2
    #2456B5
  • --color-bluebg
    #EDF3FF
  • --color-green
    #176B46
  • --color-greenbg
    #EAF6EF
  • --color-red
    #B42318
  • --color-redbg
    #FFF1F0
  • --color-redbg2
    #FEE4E2
  • --color-orange
    #9A4A0B
  • --color-orangebg
    #FFF2E5
  • --color-orangeborder
    #F2CFAE
  • --color-redborder
    #E3B7B7
  • --color-green2
    #2E8A57
  • --color-greenbg2
    #EDF5F2
  • --color-greenbg3
    #F4FAF6
  • --color-greenborder
    #CFE7D9
  • --color-cyan
    #0E7490
  • --color-cyanbg
    #ECFEFF
  • --color-white
    #FFFFFF
  • --color-black
    #000000
  • --color-gray
    #344054

아이콘

inc/icons.php 인라인 SVG. <?= icon('이름', 크기, '클래스') ?> · 색은 부모 color(currentColor). 시안 아이콘 미수령 → Remix Icon 으로 맞춤 (TODO).

  • menu
  • close
  • refresh
  • logout
  • chevron-left
  • chevron-right
  • chevron-down
  • external
  • calendar
  • plus
  • monitor
  • filter
  • download
  • car
  • search

버튼

모양(.square-btn .ico-btn .txt-btn) + 색(BTN 01 .main-btn / 02 .sub-btn / 03 .line-btn / 04 .red-btn) 조합. 이동은 <a>, 동작은 <button type="button">. 마우스를 올려 Hover 확인.

square-btn 색

코드 보기
<div class="btn-list">
    <button type="button" class="square-btn main-btn btn-txt">BTN 01</button>
    <button type="button" class="square-btn sub-btn btn-txt">BTN 02</button>
    <button type="button" class="square-btn line-btn btn-txt">BTN 03</button>
    <button type="button" class="square-btn red-btn btn-txt">BTN 04</button>
    <button type="button" class="square-btn main-btn btn-txt" disabled>Disabled</button>
</div>
<div class="btn-list">
    <a class="square-btn main-btn btn-txt" href="#none"><?= icon('plus', 18) ?>예약 등록</a>
    <a class="square-btn line-btn btn-txt" href="#none"><?= icon('monitor', 18) ?>작업장 모니터</a>
    <a class="square-btn main-btn btn-txt" href="#none"><?= icon('download', 18) ?>종합 CSV</a>
</div>

txt-btn · ico-btn

코드 보기
<a class="txt-btn body-small" href="#none">상세보기<?= icon('external', 16) ?></a>
<div class="btn-list">
    <button type="button" class="ico-btn" aria-label="새로고침"><?= icon('refresh') ?></button>
    <button type="button" class="ico-btn" aria-label="메뉴 열기"><?= icon('menu') ?></button>
    <button type="button" class="ico-btn" aria-label="닫기"><?= icon('close') ?></button>
</div>

Form

높이 40. 화면에 이름이 없으면 .sr-only label 또는 aria-label. 날짜 칸은 투명 date · month 인풋을 덮고 [data-date-text] 에 값 표시 (common.js initDateField).

custom-input · custom-select

코드 보기
<div class="btn-list">
    <label class="sr-only" for="cpKeyword">검색어</label>
    <input class="custom-input body-medium" type="text" id="cpKeyword" name="keyword" placeholder="차량번호 · 고객명" style="width: 240px;">
    <label class="sr-only" for="cpStore">점포 선택</label>
    <select class="custom-select auto body-medium" id="cpStore" name="store">
        <option value="">전체 점포</option>
        <option value="pangyo">카앤쿡 판교점</option>
    </select>
    <input class="custom-input body-medium" type="text" name="disabled_sample" value="비활성" aria-label="비활성 예시" disabled style="width: 160px;">
</div>

날짜 칸 · 날짜 이동

코드 보기
<div class="btn-list">
    <div class="date-nav">
        <button type="button" class="date-nav-btn" aria-label="이전 날"><?= icon('chevron-left', 20) ?></button>
        <button type="button" class="date-nav-today body-medium" aria-pressed="true">오늘</button>
        <button type="button" class="date-nav-btn" aria-label="다음 날"><?= icon('chevron-right', 20) ?></button>
    </div>
    <label class="date-field body-medium" data-date-field>
        <span data-date-text>2026-10-07</span><?= icon('calendar', 18) ?>
        <input type="date" value="2026-10-07" aria-label="조회 날짜">
    </label>
    <label class="date-field body-medium" data-date-field>
        <span data-date-text>2026-10</span><?= icon('calendar', 18) ?>
        <input type="month" value="2026-10" aria-label="조회 월">
    </label>
    <button type="button" class="date-field body-medium">2026-10-07 ~ 2026-10-14<?= icon('calendar', 18) ?></button>
</div>

뱃지

.badge 기본 회색 + 색 클래스 (.blue .green .cyan .orange .red .line). 상태 코드는 data/code.php → statusBadge($workStatus, 'working'). 숫자 뱃지는 .count-badge.

작업 상태

입고 대기작업 대기작업 중일시정지검수 대기작업 완료출고 완료

결제 상태 · 회원권 상태

미결제부분 결제추가 결제 필요결제 완료결제 확인 필요출고 후 미결제차감 예정차감 완료

기타

회원 비회원 점장 2건
코드 보기
<div class="btn-list">
    <span class="badge blue badge-txt">회원</span>
    <span class="badge badge-txt">비회원</span>
    <span class="badge line badge-txt">점장</span>
    <span class="count-badge badge-txt">2<span class="sr-only">건</span></span>
</div>

선택 칩 · 카드

input 을 숨기지 않고 label 로 감싼다. 칩 .choice-chip (라디오 · 체크) / 선택 카드 .select-card (라디오) / 체크 카드 .check-card (체크). 묶음은 fieldset + legend.

프로그램(필수)
세부 구성
추가 옵션 (복수 선택)
코드 보기
<fieldset class="input-item">
    <legend class="input-label req body-small">프로그램<span class="sr-only">(필수)</span></legend>
    <div class="choice-list">
        <label class="choice-chip"><input type="radio" name="cp_program" value="steam" checked><span class="btn-txt">스팀 손세차</span></label>
        <label class="choice-chip"><input type="radio" name="cp_program" value="family"><span class="btn-txt">패밀리 세이프</span></label>
        <label class="choice-chip"><input type="radio" name="cp_program" value="app" disabled><span class="btn-txt">비활성</span></label>
    </div>
</fieldset>
<fieldset class="input-item">
    <legend class="input-label body-small">세부 구성</legend>
    <div class="select-card-list">
        <label class="select-card">
            <input class="custom-radio" type="radio" name="cp_variant" value="daily" checked>
            <span class="select-card-txt"><span class="select-card-tit card-tit">데일리</span><span class="select-card-desc body-medium">내·외부 스팀 손세차 · 예상 소요 40~60분</span></span>
        </label>
        <!-- 반복 -->
        <label class="select-card"><input class="custom-radio" type="radio" name="cp_variant" value="signature"><span class="select-card-txt"><span class="select-card-tit card-tit">시그니처</span><span class="select-card-desc body-medium">왁스 · 가죽 보호제 · 센터페시아 위생관리 포함</span></span></label>
    </div>
</fieldset>
<fieldset class="input-item">
    <legend class="input-label body-small">추가 옵션 (복수 선택)</legend>
    <div class="check-card-list">
        <label class="check-card">
            <input class="custom-checkbox" type="checkbox" name="cp_option[]" value="engine">
            <span class="check-card-tit card-tit">엔진룸 클리닝·코팅</span>
            <span class="check-card-price body-medium">+50,000원</span>
        </label>
        <!-- 반복 -->
        <label class="check-card"><input class="custom-checkbox" type="checkbox" name="cp_option[]" value="sealant" checked><span class="check-card-tit card-tit">바디 실란트 왁스</span><span class="check-card-price body-medium">+20,000원</span></label>
        <label class="check-card"><input class="custom-checkbox" type="checkbox" name="cp_option[]" value="center" disabled><span class="check-card-tit card-tit">센터페시아 위생관리 (포함됨)</span><span class="check-card-price body-medium">+30,000원</span></label>
    </div>
</fieldset>

드롭다운

옵션 오른쪽에 상태를 같이 보여줘야 할 때만 사용 (그 외는 select.custom-select). [data-dropdown] · 선택값은 hidden input · 방향키 / Enter / Esc. 막힌 옵션은 aria-disabled="true".

도착 시간(필수)
코드 보기
<div class="input-item" style="max-width: 280px;">
    <span class="input-label req body-small" id="cpTimeLabel">도착 시간<span class="sr-only">(필수)</span></span>
    <div class="dropdown" data-dropdown>
        <button type="button" class="dropdown-btn body-medium" id="cpTimeBtn" aria-haspopup="listbox" aria-expanded="false" aria-controls="cpTimeList" aria-labelledby="cpTimeLabel cpTimeBtn"><span data-dropdown-text>시간 선택</span></button>
        <ul class="dropdown-list" id="cpTimeList" role="listbox" aria-labelledby="cpTimeLabel" hidden>
            <li class="dropdown-option body-medium" id="cpTime0930" role="option" aria-selected="false" data-value="09:30" data-label="09:30"><span>09:30</span><span>예약 1/2</span></li>
            <li class="dropdown-option body-medium" id="cpTime1000" role="option" aria-selected="false" aria-disabled="true" data-value="10:00" data-label="10:00"><span>10:00</span><span>수용량 마감</span></li>
            <li class="dropdown-option body-medium" id="cpTime1100" role="option" aria-selected="false" data-value="11:00" data-label="11:00"><span>11:00</span><span>예약 1/2</span></li>
        </ul>
        <input type="hidden" name="cp_time" value="">
    </div>
</div>

탭 · 토글

목록 상태 탭 role="tablist" > .tab-btn[role="tab"] (aria-selected · 건수 .tab-count). 하나만 누르는 버튼 묶음 .toggle-list[data-toggle-group] > .toggle-btn[aria-pressed].

코드 보기
<div class="tab-list" role="tablist" aria-label="작업 상태">
    <button type="button" class="tab-btn btn-txt" role="tab" data-value="all" aria-selected="true" tabindex="0">전체<span class="tab-count badge-txt">21</span></button>
    <button type="button" class="tab-btn btn-txt" role="tab" data-value="arrive" aria-selected="false" tabindex="-1">입고 대기<span class="tab-count badge-txt">9</span></button>
    <button type="button" class="tab-btn btn-txt" role="tab" data-value="done" aria-selected="false" tabindex="-1">완료<span class="tab-count badge-txt">8</span></button>
</div>
<div class="toggle-list" role="group" aria-label="예약 출처" data-toggle-group>
    <button type="button" class="toggle-btn body-medium" data-value="" aria-pressed="true">전체</button>
    <button type="button" class="toggle-btn body-medium" data-value="app" aria-pressed="false">앱</button>
    <button type="button" class="toggle-btn body-medium" data-value="naver" aria-pressed="false">네이버</button>
</div>

도움말 · 안내 · 진행

도움말 .help-wrap (마우스 올림 · 포커스 시 .help-tip) / 안내 띠 .notice-bar / 검색 칸 .search-field / 진행 막대 .progress-bar (--progress) / 정보 목록 dl.info-list.

30분 단위는 차량 도착 예약 간격입니다.

수용량 1대 적용 중 · 09:00 시간대가 기존 예약 때문에 초과 상태입니다.

고객
임채원
결제
추가 결제 필요네이버 선결제
요청
긴 문장은 .ellipsis 로 한 줄 말줄임
코드 보기
<span class="help-wrap">
    <button type="button" class="help-btn body-medium" aria-label="하루 예약 안내" aria-describedby="cpHelpTip">?</button>
    <span class="help-tip body-small" id="cpHelpTip" role="tooltip">30분 단위는 차량 도착 예약 간격입니다.</span>
</span>
<p class="notice-bar body-small">수용량 1대 적용 중 · 09:00 시간대가 기존 예약 때문에 초과 상태입니다.</p>
<div class="search-field" style="max-width: 260px;">
    <?= icon('search', 18) ?>
    <label class="sr-only" for="cpKeyword2">예약 검색</label>
    <input class="custom-input body-medium" type="search" id="cpKeyword2" name="cp_keyword" placeholder="차량번호 · 고객명 · 연락처">
</div>
<div class="progress-bar" role="progressbar" aria-label="공정 진행률" aria-valuemin="0" aria-valuemax="100" aria-valuenow="64"><span style="--progress: 64%;"></span></div>
<div class="progress-bar pause" role="progressbar" aria-label="공정 진행률 (일시정지)" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50"><span style="--progress: 50%;"></span></div>
<dl class="info-list body-medium">
    <dt>고객</dt><dd>임채원</dd>
    <dt>결제</dt><dd><span class="badge orange badge-txt">추가 결제 필요</span><span>네이버 선결제</span></dd>
    <dt>요청</dt><dd><span class="ellipsis">긴 문장은 .ellipsis 로 한 줄 말줄임</span></dd>
</dl>

모달

.modal(기본 max-width 560 / .large 900) > .modal-dim + .modal-panel(role="dialog" · tabindex="-1") > .modal-head · .modal-body(스크롤) · .modal-foot. 열기 [data-modal-open="id"] · 닫기 [data-modal-close] · Esc. 768 이하 전체 화면. 예 : 예약 등록(inc/reserve-modal.php), 수용량 변경(staff/index.php).

<div class="modal" id="cpModal" inert>
    <div class="modal-dim" data-modal-close></div>
    <div class="modal-panel" role="dialog" aria-modal="true" aria-labelledby="cpModalTit2" tabindex="-1">
        <div class="modal-head">
            <div class="modal-tit-wrap">
                <h2 class="modal-tit sub-section-tit" id="cpModalTit2">제목</h2>
                <p class="modal-desc body-medium">설명</p>
            </div>
            <button type="button" class="ico-btn modal-close" data-modal-close aria-label="닫기"><?= icon('close') ?></button>
        </div>
        <div class="modal-body"> … </div>
        <div class="modal-foot">
            <div class="btn-list">
                <button type="button" class="square-btn large line-btn btn-txt" data-modal-close>취소</button>
                <button type="button" class="square-btn large main-btn btn-txt">확인</button>
            </div>
        </div>
    </div>
</div>

박스 · 페이지 상단

흰 박스 section.section-box > .section-head(.tit-wrap + 버튼). 페이지 맨 위 시계 · 날짜 · 조회 도구는 .page-top ([data-clock] 1초마다 갱신).

page-top

2026년 10월 7일 수요일

오늘 · 판교점 영업 09:00-19:00

코드 보기
<div class="page-top">
    <div class="page-time">
        <time class="page-clock hero" data-clock>14:12:02</time>
        <div class="page-date">
            <p class="card-tit">2026년 10월 7일 수요일</p>
            <p class="page-date-desc body-large">오늘 · 판교점 영업 09:00-19:00</p>
        </div>
    </div>
    <div class="page-tool">
        <a class="square-btn main-btn btn-txt" href="#none"><?= icon('plus', 18) ?>예약 등록</a>
    </div>
</div>

section-box

현재 작업

진행 중 4대 중 2대 표시

상세보기

박스 내용

코드 보기
<section class="section-box" aria-labelledby="cpBoxDemoTit">
    <div class="section-head">
        <div class="tit-wrap">
            <h2 class="section-tit" id="cpBoxDemoTit">현재 작업</h2>
            <p class="tit-desc card-desc">진행 중 4대 중 2대 표시</p>
        </div>
        <a class="txt-btn body-small" href="#none">상세보기<?= icon('external', 16) ?></a>
    </div>
    <p class="body-medium">박스 내용</p>
</section>

요약 숫자

ul.stat-list(.col3 · .col5) > li.stat-card : p.stat-label.body-medium + p.stat-num.hero (숫자 + span.stat-unit.body-medium) + p.stat-desc.body-small. col5 는 1440 이하 3열, 768 이하 2열.

  • 결제매출

    190,000원

    전월 대비 + 10%

  • 완료 대수

    2대

    전월 대비 + 10%

  • 대당 시공금액

    80,000원

    전월 대비 + 10%

  • 신규 가입

    0명

    전월 대비 + 10%

  • 예약 증원

    0.7%

    전월 대비 + 10%

코드 보기
<ul class="stat-list col5">
    <li class="stat-card">
        <p class="stat-label body-medium">결제매출</p>
        <p class="stat-num hero">190,000<span class="stat-unit body-medium">원</span></p>
        <p class="stat-desc body-small">전월 대비 + 10%</p>
    </li>
    <!-- 반복 -->
    <li class="stat-card"><p class="stat-label body-medium">완료 대수</p><p class="stat-num hero">2<span class="stat-unit body-medium">대</span></p><p class="stat-desc body-small">전월 대비 + 10%</p></li>
    <li class="stat-card"><p class="stat-label body-medium">대당 시공금액</p><p class="stat-num hero">80,000<span class="stat-unit body-medium">원</span></p><p class="stat-desc body-small">전월 대비 + 10%</p></li>
    <li class="stat-card"><p class="stat-label body-medium">신규 가입</p><p class="stat-num hero">0<span class="stat-unit body-medium">명</span></p><p class="stat-desc body-small">전월 대비 + 10%</p></li>
    <li class="stat-card"><p class="stat-label body-medium">예약 증원</p><p class="stat-num hero">0.7<span class="stat-unit body-medium">%</span></p><p class="stat-desc body-small">전월 대비 + 10%</p></li>
</ul>
  • 확정 인건비

    850,000원

  • 같은 달 결제매출

    4,500,000원

  • 매출 대비 인건비율

    18.9%

코드 보기
<ul class="stat-list col3">
    <li class="stat-card"><p class="stat-label body-medium">확정 인건비</p><p class="stat-num hero">850,000<span class="stat-unit body-medium">원</span></p></li>
    <li class="stat-card"><p class="stat-label body-medium">같은 달 결제매출</p><p class="stat-num hero">4,500,000<span class="stat-unit body-medium">원</span></p></li>
    <li class="stat-card"><p class="stat-label body-medium">매출 대비 인건비율</p><p class="stat-num hero">18.9<span class="stat-unit body-medium">%</span></p></li>
</ul>

표

.table-wrap > table.data-table. 1024 이하 : 칸 줄바꿈 없이 .table-wrap 안에서 가로 스크롤. 글자 : thead tr.card-tit · table.body-large · 아래 줄 .cell-sub.body-small. 두 줄 셀은 아래 줄에 .cell-sub. 같은 시간대 묶음 : 첫 줄 시간 칸 rowspan, 둘째 줄부터 tr.group-row (위 선 없음). 빈 자리는 td.empty-slot.

예약 목록
시간 차량 고객 프로그램 예약 경로 작업 상태 결제 상태 · 경로 금액
09:002/2 12가 3456쏘나타 DN8 김민준회원010-0000-0000 시그니처스팀 손세차 앱 출고 완료 결제 완료앱 승인 100,000원
245더1180투싼 NX4 · M 이서연비회원010-0000-0000 데일리스팀 손세차 네이버 출고 완료 출고 후 미결제현장 결제 70,000원
10:001/2 12가 3456쏘나타 DN8 김민준비회원010-0000-0000 시그니처스팀 손세차 로드 출고 완료 회원권 차감앱 승인 회원권 1회회차 차감
빈 자리
코드 보기
<div class="table-wrap">
    <table class="data-table body-large">
        <caption class="sr-only">예약 목록</caption>
        <thead>
            <tr class="card-tit">
                <th scope="col">시간</th>
                <th scope="col">차량</th>
                <th scope="col">고객</th>
                <th scope="col">프로그램</th>
                <th scope="col">예약 경로</th>
                <th scope="col">작업 상태</th>
                <th scope="col">결제 상태 · 경로</th>
                <th scope="col">금액</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td rowspan="2">09:00<span class="cell-sub body-small">2/2</span></td>
                <td>12가 3456<span class="cell-sub body-small">쏘나타 DN8</span></td>
                <td><span class="cell-flex">김민준<span class="badge blue badge-txt">회원</span></span><span class="cell-sub body-small">010-0000-0000</span></td>
                <td>시그니처<span class="cell-sub body-small">스팀 손세차</span></td>
                <td>앱</td>
                <td><span class="badge badge-txt">출고 완료</span></td>
                <td><span class="badge green badge-txt">결제 완료</span><span class="cell-sub body-small">앱 승인</span></td>
                <td>100,000원</td>
            </tr>
            <tr class="group-row">
                <td>245더1180<span class="cell-sub body-small">투싼 NX4 · M</span></td>
                <td><span class="cell-flex">이서연<span class="badge badge-txt">비회원</span></span><span class="cell-sub body-small">010-0000-0000</span></td>
                <td>데일리<span class="cell-sub body-small">스팀 손세차</span></td>
                <td>네이버</td>
                <td><span class="badge badge-txt">출고 완료</span></td>
                <td><span class="badge red badge-txt">출고 후 미결제</span><span class="cell-sub body-small">현장 결제</span></td>
                <td>70,000원</td>
            </tr>
            <tr>
                <td rowspan="2">10:00<span class="cell-sub body-small">1/2</span></td>
                <td>12가 3456<span class="cell-sub body-small">쏘나타 DN8</span></td>
                <td><span class="cell-flex">김민준<span class="badge badge-txt">비회원</span></span><span class="cell-sub body-small">010-0000-0000</span></td>
                <td>시그니처<span class="cell-sub body-small">스팀 손세차</span></td>
                <td>로드</td>
                <td><span class="badge badge-txt">출고 완료</span></td>
                <td><span class="badge badge-txt">회원권 차감</span><span class="cell-sub body-small">앱 승인</span></td>
                <td>회원권 1회<span class="cell-sub body-small">회차 차감</span></td>
            </tr>
            <tr class="group-row">
                <td class="empty-slot" colspan="7"><span class="body-medium">빈 자리</span></td>
            </tr>
        </tbody>
    </table>
</div>

빈 상태

.empty-box > .empty-ico + .empty-desc. 목록 뒤에 hidden 으로 같이 마크업해 두고 결과가 없을 때 보인다.

작업 / 대기 중인 차량이 없습니다

코드 보기
<div class="empty-box">
    <span class="empty-ico"><?= icon('car', 22) ?></span>
    <p class="empty-desc body-medium">작업 / 대기 중인 차량이 없습니다</p>
</div>