웹 서브페이지 UI 레이아웃 작업진행

This commit is contained in:
sungro815 2026-01-09 23:01:05 +09:00
parent 746a8c5876
commit d66aded096
26 changed files with 1397 additions and 66 deletions

49
config/web.php Normal file
View File

@ -0,0 +1,49 @@
<?php
return [
'hero_slides' => [
[
'kicker' => 'PIN FOR YOU',
'title' => '안전하고 빠른 상품권 거래',
'desc' => '구글플레이·문화상품권·편의점 등 인기 상품을 할인 구매하세요.',
'cta_label' => '상품 보러가기',
'cta_url' => '/shop',
],
[
'kicker' => 'EVENT',
'title' => '카드/휴대폰 결제 지원',
'desc' => '원하는 결제수단으로 편하게, 발송은 빠르게.',
'cta_label' => '결제 안내',
'cta_url' => '/guide',
],
[
'kicker' => 'SUPPORT',
'title' => '문제 생기면 1:1 문의',
'desc' => '빠른 응답으로 도와드릴게요.',
'cta_label' => '1:1 문의하기',
'cta_url' => '/cs/qna',
],
],
'cs_tabs' => [
['label' => '공지사항', 'route' => 'web.cs.notice.index', 'key' => 'notice'],
['label' => '자주 묻는 질문', 'route' => 'web.cs.faq.index', 'key' => 'faq'],
['label' => '카카오톡상담', 'route' => 'web.cs.kakao.index', 'key' => 'kakao'],
['label' => '1:1 문의', 'route' => 'web.cs.qna.index', 'key' => 'qna'],
['label' => '이용안내', 'route' => 'web.cs.guide.index', 'key' => 'guide'],
],
'mypage_tabs' => [
['label' => '나의정보', 'route' => 'web.mypage.info.index', 'key' => 'info'],
['label' => '이용내역', 'route' => 'web.mypage.usage.index', 'key' => 'usage'],
['label' => '교환내역', 'route' => 'web.mypage.exchange.index', 'key' => 'exchange'],
['label' => '1:1문의내역', 'route' => 'web.mypage.qna.index', 'key' => 'qna'],
],
'policy_tabs' => [
['label' => '개인정보처리방침', 'route' => 'web.policy.privacy.index', 'key' => 'privacy'],
['label' => '이용약관', 'route' => 'web.policy.terms.index', 'key' => 'terms'],
['label' => '이메일무단수집거부', 'route' => 'web.policy.email.index', 'key' => 'email'],
],
];

View File

@ -1212,3 +1212,541 @@ h1, h2, h3, h4, h5, h6 {
.notice-link{ grid-template-columns: 44px 1fr; } .notice-link{ grid-template-columns: 44px 1fr; }
.notice-date{ display:none; } .notice-date{ display:none; }
} }
/* ===== Subpage Skeleton ===== */
.subpage-wrap{ padding: 18px 0 26px; }
.subpage-header{
display:flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin: 40px 0 16px;
padding-left:8px;
}
.subpage-title{
font-size: 20px;
font-weight: 900;
letter-spacing: -0.3px;
color:#0F172A;
}
.subpage-desc{
font-size: 13px;
color:#64748B;
white-space: nowrap;
overflow:hidden;
text-overflow: ellipsis;
max-width: 55%;
}
.breadcrumb{ margin-top: 6px; }
.breadcrumb-list{
display:flex;
gap: 8px;
flex-wrap: wrap;
font-size: 12px;
color:#94A3B8;
}
.breadcrumb-item::after{
content:"/";
margin-left: 8px;
color:#CBD5E1;
}
.breadcrumb-item:last-child::after{ content:none; }
.breadcrumb-link{ color:#64748B; }
.breadcrumb-link:hover{ color:#2563EB; }
.breadcrumb-current{ color:#475569; font-weight:700; }
.subpage-grid{
display:grid;
grid-template-columns: 260px 1fr;
gap: 14px;
align-items: start;
}
.subpage-tabs{ display:none; }
.subpage-side{ position: sticky; top: 90px; }
.content-card{
background:#FFFFFF;
border: 1px solid #E5E7EB;
border-radius: 16px;
padding: 16px;
box-shadow: 0 1px 2px rgba(15,23,42,.04);
}
/* side nav */
.subnav--side .subnav-list{
background:#FFFFFF;
border: 1px solid #E5E7EB;
border-radius: 16px;
padding: 10px;
display:grid;
gap: 6px;
}
.subnav-link{
display:flex;
align-items:center;
height: 40px;
padding: 0 12px;
border-radius: 12px;
font-size: 13px;
font-weight: 800;
color:#334155;
}
.subnav-link:hover{ background:#F8FAFC; }
.subnav-link.is-active{
background:#EFF6FF;
border: 1px solid #BFDBFE;
color:#1D4ED8;
}
/* mobile tabs */
.subnav--tabs{
display:flex;
gap: 8px;
overflow-x: auto;
scrollbar-width: none;
padding-bottom: 6px;
}
.subnav--tabs::-webkit-scrollbar{ display:none; }
.subnav-tab{
flex: 0 0 auto;
height: 36px;
padding: 0 14px;
border-radius: 9999px;
border: 1px solid #E5E7EB;
background:#FFFFFF;
font-size: 12.5px;
font-weight: 900;
color:#334155;
}
.subnav-tab:hover{ background:#F8FAFC; }
.subnav-tab.is-active{
background:#EFF6FF;
border-color:#BFDBFE;
color:#1D4ED8;
}
/* notice sample page */
.list-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom: 10px; }
.list-title{ font-size: 14px; font-weight: 900; color:#0F172A; margin:0; }
.list-desc{ font-size: 12.5px; color:#64748B; margin-top: 4px; }
.list-head{
display:flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin-bottom: 10px;
}
.list-title{
margin: 0;
}
.list-desc{
margin: 0;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 60%;
font-size: 12.5px; color:#64748B;
}
@media (max-width: 560px){
.list-desc{ display:none; } /* 모바일은 깔끔하게 */
}
.notice-list2{ display:grid; }
.notice-list2 li + li{ border-top: 1px solid #EEF2F7; }
.notice-list2 a{
display:grid;
grid-template-columns: 54px 1fr auto;
gap: 10px;
align-items:center;
padding: 12px 6px;
border-radius: 12px;
}
.notice-list2 a:hover{ background:#F8FAFC; }
.n2-tag{
height: 20px;
display:inline-flex;
align-items:center;
justify-content:center;
padding: 0 10px;
border-radius: 9999px;
font-size: 11px;
font-weight: 900;
background:#EFF6FF;
border:1px solid #BFDBFE;
color:#1D4ED8;
}
.n2-tag--info{
background:#F1F5F9;
border-color:#E2E8F0;
color:#334155;
}
.n2-text{
font-size: 13px;
font-weight: 800;
color:#334155;
white-space: nowrap;
overflow:hidden;
text-overflow: ellipsis;
}
.n2-date{ font-size: 12px; color:#94A3B8; white-space: nowrap; }
.n2s-text{
font-size: 13px;
font-weight: 600;
color: #2a3647;
white-space: nowrap;
overflow:hidden;
text-overflow: ellipsis;
}
@media (max-width: 1024px){
.subpage-grid{ grid-template-columns: 1fr; }
.subpage-side{ display:none; }
.subpage-tabs{ display:block; }
.subpage-desc{ max-width: 100%; }
}
@media (max-width: 560px){
.notice-list2 a{ grid-template-columns: 54px 1fr; }
.n2-date{ display:none; }
}
/* ===== Sub Hero (compact rolling banner) ===== */
.subhero{
margin: 8px 0 10px;
}
.subhero-inner{
position: relative;
overflow: hidden;
border-radius: 16px;
border: 1px solid #E5E7EB;
background: #F8FAFC;
height: 160px; /* ✅ 메인보다 낮게 */
}
.subhero-track{
display:flex;
height: 100%;
transition: transform .45s cubic-bezier(.25,1,.5,1);
}
.subhero-slide{
min-width: 100%;
height: 100%;
background-image: var(--bg);
background-size: cover;
background-position: center;
position: relative;
}
.subhero-slide::after{
content:"";
position:absolute; inset:0;
background: rgba(255,255,255,.72); /* ✅ 과하지 않게 텍스트 가독 */
}
.subhero-content{
position: relative;
z-index: 1;
height: 100%;
padding: 18px 18px;
display:flex;
flex-direction: column;
justify-content: center;
max-width: 720px;
}
.subhero-kicker{
font-size: 11px;
font-weight: 900;
letter-spacing: .8px;
color: #2563EB;
}
.subhero-title{
margin-top: 6px;
font-size: 20px;
font-weight: 900;
color:#0F172A;
letter-spacing: -0.3px;
}
.subhero-desc{
margin-top: 6px;
font-size: 13px;
color:#64748B;
}
.subhero-cta{
margin-top: 10px;
display:inline-flex;
align-items:center;
height: 34px;
padding: 0 12px;
border-radius: 9999px;
border: 1px solid #BFDBFE;
background: #EFF6FF;
color:#1D4ED8;
font-size: 12.5px;
font-weight: 900;
width: fit-content;
}
.subhero-cta:hover{ background:#2563EB; border-color:#2563EB; color:#fff; }
.subhero-arrow{
position:absolute; top:50%;
transform: translateY(-50%);
width: 34px; height: 34px;
border-radius: 9999px;
border: 1px solid #E5E7EB;
background: rgba(255,255,255,.9);
font-size: 20px;
font-weight: 900;
color:#0F172A;
}
.subhero-arrow.prev{ left: 10px; }
.subhero-arrow.next{ right: 10px; }
.subhero-dots{
position:absolute; bottom: 10px; left: 50%;
transform: translateX(-50%);
display:flex; gap: 6px;
}
.subhero-dot{
width: 7px; height: 7px;
border-radius: 9999px;
background: rgba(15,23,42,.18);
}
.subhero-dot.active{ background:#2563EB; transform: scale(1.2); }
/* ===== Notice toolbar (no duplicate nav) ===== */
.notice-toolbar{
display:flex;
align-items:flex-start;
justify-content:space-between;
gap: 12px;
margin: 6px 0 14px;
padding: 12px 12px;
border: 1px solid #E5E7EB;
border-radius: 16px;
background: #FFFFFF;
box-shadow: 0 1px 2px rgba(15,23,42,.04);
}
.nt-left{
min-width: 0;
display:flex;
flex-direction:column;
gap: 8px;
}
.nt-pill{
width: fit-content;
height: 22px;
padding: 0 10px;
border-radius: 9999px;
background: #EFF6FF;
border: 1px solid #BFDBFE;
color: #1D4ED8;
font-size: 11px;
font-weight: 900;
}
.nt-meta{
font-size: 12.5px;
color: #64748B;
line-height: 1.45;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 520px;
}
.nt-right{
display:flex;
flex-direction:column;
gap: 10px;
align-items:flex-end;
}
.nt-search{
display:flex;
align-items:center;
gap: 8px;
}
.nt-search input{
width: 280px;
height: 38px;
padding: 0 12px;
border-radius: 9999px;
border: 1px solid #E5E7EB;
background: #F3F4F6;
font-size: 13px;
}
.nt-search input:focus{
outline: none;
border-color: #2563EB;
background: #FFFFFF;
box-shadow: 0 0 0 2px #EFF6FF;
}
.nt-search button{
height: 38px;
padding: 0 12px;
border-radius: 9999px;
background: #2563EB;
color:#fff;
font-size: 13px;
font-weight: 900;
border: 1px solid #2563EB;
}
.nt-filters{
display:flex;
gap: 8px;
flex-wrap: wrap;
justify-content:flex-end;
}
.nt-chip{
height: 32px;
padding: 0 12px;
border-radius: 9999px;
border: 1px solid #E5E7EB;
background: #FFFFFF;
color: #334155;
font-size: 12.5px;
font-weight: 900;
}
.nt-chip:hover{ background:#F8FAFC; }
.nt-chip.is-active{
background:#EFF6FF;
border-color:#BFDBFE;
color:#1D4ED8;
}
@media (max-width: 1024px){
.notice-toolbar{ flex-direction:column; align-items:stretch; }
.nt-meta{ max-width: 100%; white-space: normal; overflow: visible; text-overflow: clip; }
.nt-right{ align-items:stretch; }
.nt-search input{ width: 100%; }
.nt-filters{ justify-content:flex-start; }
}
/* ===== Hero variants ===== */
.hero-slider{ position: relative; overflow: hidden; background: var(--color-bg-section); }
.hero-slider--main{ height: 400px; }
.hero-slider--compact{ height: 170px; border-radius: 16px; border: 1px solid #E5E7EB; margin: 14px 0 18px; }
/* track/slide 동일 */
.hero-track{ display:flex; height:100%; transition: transform 0.5s cubic-bezier(0.25,1,0.5,1); }
.hero-slide{ min-width:100%; height:100%; display:flex; align-items:center; justify-content:center; padding: 0 24px; }
.hero-content{ max-width: var(--container-width); width:100%; text-align:left; padding: 0 8px; }
/* ✅ compact에서 타이포/여백만 줄이기 */
.hero-slider--compact .hero-slide{ padding: 0 18px; }
.hero-slider--compact .hero-title{ font-size: 22px; margin-bottom: 6px; }
.hero-slider--compact .hero-desc{ font-size: 13px; margin-bottom: 10px; }
.hero-slider--compact .btn.hero-cta{ padding: 8px 14px; font-size: 13px; }
/* ✅ 왼쪽/오른쪽 화살표 버튼이 컨텐츠를 가리지 않게 여백 확보 */
.slider-arrow{
position:absolute; top:50%; transform: translateY(-50%);
width: 34px; height: 34px;
border-radius: 9999px;
border: 1px solid #E5E7EB;
background: rgba(255,255,255,.9);
font-size: 20px; font-weight: 900;
display:flex; align-items:center; justify-content:center;
z-index: 5;
}
.slider-arrow.prev{ left: 10px; }
.slider-arrow.next{ right: 10px; }
/* ✅ 컨텐츠 좌우 패딩(화살표 영역만큼) */
.hero-slider--compact .hero-content{ padding-left: 44px; padding-right: 44px; }
/* dots */
.dots-container{ position:absolute; bottom: 12px; left:50%; transform: translateX(-50%); display:flex; gap: 8px; z-index:5; }
.dot{ width: 8px; height: 8px; border-radius: 50%; background: rgba(0,0,0,0.18); }
.dot.active{ background: var(--color-accent-blue); transform: scale(1.15); }
@media (max-width: 768px){
.hero-slider--compact{ height: 150px; }
.hero-slider--compact .hero-content{ padding-left: 14px; padding-right: 14px; } /* 모바일에선 화살표가 위에 떠도 괜찮게 */
.slider-arrow{ display:none; } /* 모바일은 스와이프/도트로만 */
}
.subpage-wrap .hero-slider--compact{
margin-top: 14px;
margin-bottom: 16px;
}
/* ===== List Head (section heading) ===== */
.list-head{
display:flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin: 6px 0 12px;
/* 은은한 구획감: 라인 + 여백만 */
padding-bottom: 8px;
border-bottom: 1px solid #EEF2F7;
}
.list-title{
margin: 0;
font-size: 16px; /* 과하게 크지 않게 */
font-weight: 900;
letter-spacing: -0.2px;
color: #0F172A;
line-height: 1.2;
}
.list-desc{
margin: 0;
font-size: 13px;
color: #64748B;
line-height: 1.2;
/* 우측 정렬 + 길면 깔끔하게 처리 */
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 60%;
}
/* 모바일은 설명 숨기고 더 타이트하게 */
@media (max-width: 560px){
.list-head{ padding-bottom: 6px; margin: 4px 0 10px; }
.list-title{ font-size: 15px; }
.list-desc{ display:none; }
}
/* ===== CS Tabs ===== */
.cs-tabs{
display:flex;
gap: 8px;
flex-wrap: wrap;
margin: 10px 0 14px;
}
.cs-tab{
height: 34px;
padding: 0 14px;
border-radius: 9999px;
border: 1px solid #E5E7EB;
background:#fff;
font-size: 13px;
font-weight: 800;
color:#334155;
}
.cs-tab:hover{ background:#F8FAFC; }
.cs-tab.is-active{
background:#EFF6FF;
border-color:#BFDBFE;
color:#1D4ED8;
}

View File

@ -125,4 +125,45 @@ document.addEventListener('DOMContentLoaded', () => {
alert('모바일 메뉴 드로어 열림 (구현 예정)'); alert('모바일 메뉴 드로어 열림 (구현 예정)');
}); });
} }
// --- Sub Hero Carousel ---
(function initSubHero(){
const wrap = document.querySelector('.subhero-inner');
if(!wrap) return;
const track = wrap.querySelector('.subhero-track');
const slides = wrap.querySelectorAll('.subhero-slide');
const prev = wrap.querySelector('.subhero-arrow.prev');
const next = wrap.querySelector('.subhero-arrow.next');
const dots = wrap.querySelectorAll('.subhero-dot');
let idx = 0;
const total = slides.length;
if(total <= 1) return;
const render = () => {
track.style.transform = `translateX(-${idx * 100}%)`;
dots.forEach((d,i)=>d.classList.toggle('active', i===idx));
};
const goNext = () => { idx = (idx + 1) % total; render(); };
const goPrev = () => { idx = (idx - 1 + total) % total; render(); };
let t = setInterval(goNext, 6000);
const reset = () => { clearInterval(t); t = setInterval(goNext, 6000); };
next?.addEventListener('click', ()=>{ goNext(); reset(); });
prev?.addEventListener('click', ()=>{ goPrev(); reset(); });
dots.forEach(d => d.addEventListener('click', ()=>{
idx = Number(d.dataset.index || 0);
render(); reset();
}));
wrap.addEventListener('mouseenter', ()=>clearInterval(t));
wrap.addEventListener('mouseleave', ()=>{ t = setInterval(goNext, 6000); });
render();
})();
}); });

View File

@ -13,8 +13,8 @@
<a href="/" class="nav-link">HOME</a> <a href="/" class="nav-link">HOME</a>
<a href="/shop" class="nav-link nav-link--exchange">SHOP</a> <a href="/shop" class="nav-link nav-link--exchange">SHOP</a>
{{-- <a href="/exchange" class="nav-link nav-link--exchange">상품권현금교환</a>--}} {{-- <a href="/exchange" class="nav-link nav-link--exchange">상품권현금교환</a>--}}
<a href="/mypage" class="nav-link">마이페이지</a> <a href="/mypage/info" class="nav-link">마이페이지</a>
<a href="/cs" class="nav-link">고객센터</a> <a href="/cs/notice" class="nav-link">고객센터</a>
</nav> </nav>
</div> </div>

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '자주 묻는 질문';
$pageDesc = '결제/발송/환불 등 자주 문의되는 내용을 빠르게 확인하세요.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '고객센터', 'url' => url('/cs')],
['label' => '자주 묻는 질문', 'url' => url()->current()],
];
$csActive = 'faq';
@endphp
@extends('web.layouts.subpage')
@section('title', '자주 묻는 질문(FAQ) | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU FAQ입니다. 결제/발송/환불/이용 관련 자주 묻는 질문을 확인하세요.')
@section('canonical', url('/cs/faq'))
@section('subcontent')
<div class="faq-page">
@include('web.partials.content-head', [
'title' => 'FAQ',
'desc' => '원하시는 항목을 선택해 빠르게 해결해 보세요.'
])
{{-- TODO: FAQ 내용(아코디언/카테고리/검색 ) --}}
</div>
@endsection

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '이용안내';
$pageDesc = '구매부터 발송/환불까지 서비스 이용 방법을 한눈에 안내합니다.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '고객센터', 'url' => url('/cs')],
['label' => '이용안내', 'url' => url()->current()],
];
$csActive = 'guide';
@endphp
@extends('web.layouts.subpage')
@section('title', '이용안내 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 이용안내입니다. 상품권 구매/발송/환불 등 이용 방법을 확인하세요.')
@section('canonical', url('/cs/guide'))
@section('subcontent')
<div class="guide-page">
@include('web.partials.content-head', [
'title' => '서비스 이용 안내',
'desc' => '주요 흐름(구매 → 결제 → 발송 → 환불)을 기준으로 안내합니다.'
])
{{-- TODO: 이용안내 본문(단계별 가이드/주의사항/정책 링크 ) --}}
</div>
@endsection

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '카카오톡 상담';
$pageDesc = '카카오톡으로 빠르고 편하게 문의하실 수 있어요.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '고객센터', 'url' => url('/cs')],
['label' => '카카오톡 상담', 'url' => url()->current()],
];
$csActive = 'kakao';
@endphp
@extends('web.layouts.subpage')
@section('title', '카카오톡 상담 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 카카오톡 상담 안내입니다. 운영시간과 상담 절차를 확인하세요.')
@section('canonical', url('/cs/kakao'))
@section('subcontent')
<div class="kakao-page">
@include('web.partials.content-head', [
'title' => '카카오톡 상담 안내',
'desc' => '운영시간과 안내사항을 확인하고 빠르게 문의해 주세요.'
])
{{-- TODO: 카카오 채널 링크/QR/운영시간/주의사항 --}}
</div>
@endsection

View File

@ -0,0 +1,66 @@
@php
$pageTitle = '공지사항';
$pageDesc = '서비스 운영 및 결제/발송 관련 안내를 확인하세요.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '고객센터', 'url' => url('/cs')],
['label' => '공지사항', 'url' => url()->current()],
];
// ✅ 이것만 남기면 됨 (탭 목록은 config에서 자동)
$csActive = 'notice';
@endphp
@extends('web.layouts.subpage')
@section('title', '공지사항 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 공지사항입니다. 운영/결제/발송 관련 최신 안내를 확인하세요.')
@section('canonical', url('/cs/notice'))
@section('subcontent')
<div class="notice-page">
@include('web.partials.content-head', [
'title' => '최신 공지',
'desc' => '중요 안내를 빠르게 확인하세요.'
])
<ul class="notice-list2">
{{-- TODO: DB 연결 더미 --}}
<li><a href="/cs/notice/1"><span class="n2-tag">공지</span><span class="n2-text"> 연휴 고객센터 운영 안내</span><time class="n2-date">2026.01.09</time></a></li>
<li><a href="/cs/notice/2"><span class="n2-tag n2-tag--info">안내</span><span class="n2-text">휴대폰 결제 점검 안내(일시)</span><time class="n2-date">2026.01.08</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내핀번호 발송/재발송 정책 안내핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">발송</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">경고</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">당첨자</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
<li><a href="/cs/notice/3"><span class="n2-tag n2-tag--info">안내</span><span class="n2s-text">핀번호 발송/재발송 정책 안내</span><time class="n2-date">2026.01.06</time></a></li>
</ul>
{{-- Notice Toolbar (replace sub-quick) --}}
<div class="notice-toolbar" role="search">
<div class="nt-left">
<span class="nt-meta">운영시간 평일 09:00~18:00 · 긴급 문의는 1:1 문의 이용</span>
</div>
<form class="nt-right" action="{{ url('/cs/notice') }}" method="GET">
<div class="nt-search">
<input type="text" name="q" value="{{ request('q') }}"
placeholder="검색"
aria-label="공지 검색">
<button type="submit" aria-label="검색">검색</button>
</div>
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '1:1 문의';
$pageDesc = '개인 문의는 1:1로 남겨주시면 순차적으로 답변드립니다.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '고객센터', 'url' => url('/cs')],
['label' => '1:1 문의', 'url' => url()->current()],
];
$csActive = 'qna';
@endphp
@extends('web.layouts.subpage')
@section('title', '1:1 문의 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 1:1 문의 페이지입니다. 문의를 남겨주시면 순차적으로 답변드립니다.')
@section('canonical', url('/cs/qna'))
@section('subcontent')
<div class="qna-page">
@include('web.partials.content-head', [
'title' => '문의하기',
'desc' => '문의 내용을 남겨주시면 확인 후 빠르게 안내드리겠습니다.'
])
{{-- TODO: 문의 / 문의 내역 / 로그인 여부 처리 --}}
</div>
@endsection

View File

@ -3,7 +3,17 @@
@section('content') @section('content')
{{-- Hero --}} {{-- Hero --}}
@include('web.main.hero-carousel') @php
$heroSlides = config('web.hero_slides', []);
@endphp
@if(!empty($heroSlides))
@include('web.partials.hero-slider', [
'slides' => $heroSlides,
'variant' => 'main',
'id' => 'hero-main'
])
@endif
{{-- Quick Categories --}} {{-- Quick Categories --}}
@include('web.main.quick-categories') @include('web.main.quick-categories')

View File

@ -4,22 +4,54 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voucher Mall - 상품권 최저가 쇼핑</title> {{-- CSRF --}}
{{-- SEO --}}
<meta name="description" content="상품권, 모바일 교환권, 구글플레이, 문화상품권 등 다양한 모바일 쿠폰 최저가 할인 쇼핑몰">
<link rel="canonical" href="{{ rtrim(config('app.url'), '/') }}/">
{{-- CSRF (추후 /로그인 대비) --}}
<meta name="csrf-token" content="{{ csrf_token() }}"> <meta name="csrf-token" content="{{ csrf_token() }}">
{{-- Pretendard Font --}} {{-- Canonical/Title/Description (중복 금지) --}}
<link rel="stylesheet" href="{{ asset('css/pretendard.css') }}"> @php
<link rel="preload" href="{{ asset('assets/fonts/pretendard/PretendardVariable.woff2') }}" as="font" type="font/woff2" crossorigin> $defaultTitle = 'PIN FOR YOU';
$defaultDesc = '상품권, 모바일 교환권, 구글플레이, 문화상품권 등 다양한 모바일 쿠폰을 안전하고 빠르게 할인 구매하세요.';
$canonicalUrl = trim($__env->yieldContent('canonical', url()->current()));
@endphp
{{-- WEB 전용 번들 로딩 --}} <title>@yield('title', $defaultTitle)</title>
<meta name="description" content="@yield('meta_description', $defaultDesc)">
<meta name="robots" content="@yield('robots', 'index,follow')">
<link rel="canonical" href="{{ $canonicalUrl }}">
{{-- Open Graph --}}
<meta property="og:site_name" content="PIN FOR YOU">
<meta property="og:title" content="@yield('og_title', trim($__env->yieldContent('title', $defaultTitle)))">
<meta property="og:description" content="@yield('og_description', trim($__env->yieldContent('meta_description', $defaultDesc)))">
<meta property="og:url" content="@yield('og_url', $canonicalUrl)">
<meta property="og:type" content="@yield('og_type', 'website')">
<meta property="og:locale" content="ko_KR">
{{-- 대표 이미지 준비되면 사용 (없으면 생략 가능)
<meta property="og:image" content="@yield('og_image', asset('assets/images/common/og-default.png'))">
--}}
{{-- Twitter Card (선택이지만 권장) --}}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="@yield('tw_title', trim($__env->yieldContent('title', $defaultTitle)))">
<meta name="twitter:description" content="@yield('tw_description', trim($__env->yieldContent('meta_description', $defaultDesc)))">
{{-- <meta name="twitter:image" content="@yield('tw_image', asset('assets/images/common/og-default.png'))"> --}}
{{-- (선택) 기본 키워드는 의미 없어서 생략 권장 --}}
{{-- <meta name="keywords" content="..."> --}}
{{-- Font --}}
<link rel="stylesheet" href="{{ asset('css/pretendard.css') }}">
<link rel="preload"
href="{{ asset('assets/fonts/pretendard/PretendardVariable.woff2') }}"
as="font" type="font/woff2" crossorigin>
{{-- Web Bundle --}}
@vite(['resources/css/web.css', 'resources/js/web.js']) @vite(['resources/css/web.css', 'resources/js/web.js'])
{{-- 페이지별 추가 head --}}
@yield('head')
</head> </head>
<body> <body>
{{-- Header --}} {{-- Header --}}

View File

@ -0,0 +1,143 @@
@extends('web.layouts.layout')
@section('content')
<div class="subpage-wrap">
<div class="container">
{{-- Breadcrumb (optional) --}}
@isset($breadcrumbs)
@include('web.partials.breadcrumb', ['items' => $breadcrumbs])
@endisset
@php
$heroSlides = config('web.hero_slides', []);
// ✅ 섹션 감지
$isCsPage = request()->is('cs') || request()->is('cs/*') || request()->routeIs('web.cs.*');
$isMypagePage = request()->is('mypage') || request()->is('mypage/*') || request()->routeIs('web.mypage.*');
$isPolicyPage = request()->is('policy') || request()->is('policy/*') || request()->routeIs('web.policy.*');
// ✅ CS subnav 자동 주입
if ($isCsPage && empty($subnavItems)) {
$subnavItems = collect(config('web.cs_tabs', []))
->map(function ($t) {
$url = '#';
if (!empty($t['route']) && \Illuminate\Support\Facades\Route::has($t['route'])) {
$url = route($t['route']);
}
return [
'label' => $t['label'] ?? '',
'url' => $url,
'key' => $t['key'] ?? null,
];
})
->values()
->all();
}
// ✅ MYPAGE subnav 자동 주입
if ($isMypagePage && empty($subnavItems)) {
$subnavItems = collect(config('web.mypage_tabs', []))
->map(function ($t) {
$url = '#';
if (!empty($t['route']) && \Illuminate\Support\Facades\Route::has($t['route'])) {
$url = route($t['route']);
}
return [
'label' => $t['label'] ?? '',
'url' => $url,
'key' => $t['key'] ?? null,
];
})
->values()
->all();
}
// ✅ PolicyPage subnav 자동 주입
if ($isPolicyPage && empty($subnavItems)) {
$subnavItems = collect(config('web.policy_tabs', []))
->map(function ($t) {
$url = '#';
if (!empty($t['route']) && \Illuminate\Support\Facades\Route::has($t['route'])) {
$url = route($t['route']);
}
return [
'label' => $t['label'] ?? '',
'url' => $url,
'key' => $t['key'] ?? null,
];
})
->values()
->all();
}
// ✅ active key 우선순위: 각 섹션 전용 변수 -> 기존 subnavActive
$subnavActive = $csActive ?? $mypageActive ?? ($subnavActive ?? null);
@endphp
@if(!empty($heroSlides))
@include('web.partials.hero-slider', [
'slides' => $heroSlides,
'variant' => 'compact',
'id' => 'hero-sub'
])
@endif
{{-- Header --}}
@include('web.partials.subpage-header', [
'title' => $pageTitle ?? '페이지 제목',
'desc' => $pageDesc ?? null
])
{{-- Body --}}
<div class="subpage-grid">
{{-- 고객센터면 cs-tabs --}}
@if($isCsPage)
@include('web.partials.cs-tabs', [
'activeKey' => $subnavActive
])
{{-- 마이페이지면 mypage-tabs --}}
@elseif($isMypagePage)
@include('web.partials.mypage-tabs', [
'activeKey' => $subnavActive
])
@elseif($isPolicyPage)
@include('web.partials.policy-tabs', [
'activeKey' => $subnavActive
])
{{-- 일반 서브페이지는 기존 방식 유지 --}}
@else
{{-- Mobile Tabs --}}
<div class="subpage-tabs">
@include('web.partials.subpage-sidenav', [
'items' => $subnavItems ?? [],
'active' => $subnavActive ?? null,
'mode' => 'tabs'
])
</div>
{{-- Desktop Side --}}
<aside class="subpage-side" aria-label="서브메뉴">
@include('web.partials.subpage-sidenav', [
'items' => $subnavItems ?? [],
'active' => $subnavActive ?? null,
'mode' => 'side'
])
</aside>
@endif
{{-- Main --}}
<main class="subpage-main" id="main-content">
<div class="content-card">
@yield('subcontent')
</div>
</main>
</div>
</div>
</div>
@endsection

View File

@ -1,52 +0,0 @@
<section class="hero-slider" aria-label="프로모션 배너">
<div class="hero-track">
<!-- Slide 1 -->
<div class="hero-slide" style="background: linear-gradient(135deg, #EFF6FF 0%, #FFFFFF 100%);">
<div class="container hero-content">
<span style="color: var(--color-accent-blue); font-weight: 700; margin-bottom: 8px; display: block;">특별
프로모션</span>
<h1 class="hero-title">구글플레이 기프트카드<br>최대 12% 즉시 할인</h1>
<p class="hero-desc">인기 게임 아이템부터 영화까지, 저렴하게 즐기세요.</p>
<a href="/shop?category=google" class="btn btn-primary">지금 구매하기</a>
</div>
</div>
<!-- Slide 2 -->
<div class="hero-slide" style="background: linear-gradient(135deg, #F0FDFA 0%, #FFFFFF 100%);">
<div class="container hero-content">
<span style="color: #059669; font-weight: 700; margin-bottom: 8px; display: block;">신규 입점</span>
<h1 class="hero-title">문화상품권 24시간<br>자동 발송 시스템 오픈</h1>
<p class="hero-desc">기다림 없이 결제 즉시 문자로 핀번호를 받아보세요.</p>
<a href="/shop?category=paper" class="btn btn-primary" style="background-color: #059669;">상품 보러가기</a>
</div>
</div>
<!-- Slide 3 -->
<div class="hero-slide" style="background: linear-gradient(135deg, #FFF7ED 0%, #FFFFFF 100%);">
<div class="container hero-content">
<span style="color: #EA580C; font-weight: 700; margin-bottom: 8px; display: block;">한정 수량</span>
<h1 class="hero-title">편의점 모바일 금액권<br>5만원권 10% 핫딜</h1>
<p class="hero-desc">CU, GS25, 세븐일레븐 전국 어디서나 사용 가능</p>
<a href="/shop?category=convenience" class="btn btn-primary" style="background-color: #EA580C;">구매하기</a>
</div>
</div>
</div>
<button class="slider-arrow prev" aria-label="이전 슬라이드"
style="position: absolute; left: 24px; top: 50%; transform: translateY(-50%); width: 40px; height: 40px; border-radius: 50%; background: white; box-shadow: var(--shadow-md); display: flex; align-items: center; justify-content: center;">
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
</button>
<button class="slider-arrow next" aria-label="다음 슬라이드"
style="position: absolute; right: 24px; top: 50%; transform: translateY(-50%); width: 40px; height: 40px; border-radius: 50%; background: white; box-shadow: var(--shadow-md); display: flex; align-items: center; justify-content: center;">
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
<div class="dots-container">
<!-- Script will populate docs based on slide count -->
<div class="dot active"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</section>

View File

@ -0,0 +1,33 @@
@props(['items' => []])
<nav class="breadcrumb" aria-label="breadcrumb">
<ol class="breadcrumb-list">
@foreach($items as $i => $it)
<li class="breadcrumb-item">
@if(!empty($it['url']) && $i < count($items)-1)
<a href="{{ $it['url'] }}" class="breadcrumb-link">{{ $it['label'] }}</a>
@else
<span class="breadcrumb-current" aria-current="page">{{ $it['label'] }}</span>
@endif
</li>
@endforeach
</ol>
</nav>
{{-- SEO: Breadcrumb JSON-LD --}}
@php
$ld = [
"@context" => "https://schema.org",
"@type" => "BreadcrumbList",
"itemListElement" => []
];
foreach($items as $idx => $it){
$ld["itemListElement"][] = [
"@type" => "ListItem",
"position" => $idx+1,
"name" => $it["label"],
"item" => $it["url"] ?? url()->current()
];
}
@endphp
<script type="application/ld+json">{!! json_encode($ld, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) !!}</script>

View File

@ -0,0 +1,8 @@
@props(['title' => '', 'desc' => null])
<div class="list-head">
<h2 class="list-title">{{ $title }}</h2>
@if($desc)
<p class="list-desc">{{ $desc }}</p>
@endif
</div>

View File

@ -0,0 +1,44 @@
@php
// config에서 CS 탭 가져와서, 기존 subpage-sidenav가 기대하는 형식(label,url,key)으로 변환
$items = collect(config('web.cs_tabs', []))
->map(function ($t) {
return [
'label' => $t['label'] ?? '',
'url' => isset($t['route']) ? route($t['route']) : '#',
'key' => $t['key'] ?? null,
];
})
->values()
->all();
// activeKey는 페이지에서 넘기거나, 없으면 현재 route로 자동 판별도 가능
$activeKey = $activeKey ?? null;
if (!$activeKey) {
// route 기반 자동 active (routeIs가 동작하려면 routes에 name이 있어야 함)
foreach (config('web.cs_tabs', []) as $t) {
if (!empty($t['route']) && request()->routeIs($t['route'])) {
$activeKey = $t['key'] ?? null;
break;
}
}
}
@endphp
{{-- Mobile Tabs --}}
<div class="subpage-tabs">
@include('web.partials.subpage-sidenav', [
'items' => $items,
'active' => $activeKey,
'mode' => 'tabs'
])
</div>
{{-- Desktop Side --}}
<aside class="subpage-side" aria-label="고객센터 메뉴">
@include('web.partials.subpage-sidenav', [
'items' => $items,
'active' => $activeKey,
'mode' => 'side'
])
</aside>

View File

@ -0,0 +1,43 @@
@props([
'slides' => [],
'variant' => 'main', // 'main' | 'compact'
'id' => 'hero'
])
@if(!empty($slides))
<section class="hero-slider hero-slider--{{ $variant }}" id="{{ $id }}" aria-label="프로모션 배너">
<div class="hero-track">
@foreach($slides as $s)
<div class="hero-slide">
<div class="hero-content">
@if(!empty($s['kicker']))
<div class="hero-kicker">{{ $s['kicker'] }}</div>
@endif
<h2 class="hero-title">{{ $s['title'] }}</h2>
@if(!empty($s['desc']))
<p class="hero-desc">{{ $s['desc'] }}</p>
@endif
@if(!empty($s['cta_label']) && !empty($s['cta_url']))
<a href="{{ $s['cta_url'] }}" class="btn btn-primary hero-cta">
{{ $s['cta_label'] }}
</a>
@endif
</div>
</div>
@endforeach
</div>
@if(count($slides) > 1)
<button class="slider-arrow prev" aria-label="이전 배너"></button>
<button class="slider-arrow next" aria-label="다음 배너"></button>
<div class="dots-container" aria-label="배너 선택">
@foreach($slides as $idx => $s)
<button class="dot {{ $idx===0?'active':'' }}" data-index="{{ $idx }}" aria-label="배너 {{ $idx+1 }}"></button>
@endforeach
</div>
@endif
</section>
@endif

View File

@ -0,0 +1,45 @@
@php
$rawTabs = config('web.mypage_tabs', []);
$items = collect($rawTabs)->map(function ($t) {
$url = '#';
if (!empty($t['route']) && \Illuminate\Support\Facades\Route::has($t['route'])) {
$url = route($t['route']);
}
return [
'label' => $t['label'] ?? '',
'url' => $url,
'key' => $t['key'] ?? null,
];
})->values()->all();
$activeKey = $activeKey ?? null;
// activeKey가 없으면 현재 라우트로 자동 판별
if (!$activeKey) {
foreach ($rawTabs as $t) {
if (!empty($t['route']) && request()->routeIs($t['route'])) {
$activeKey = $t['key'] ?? null;
break;
}
}
}
@endphp
{{-- Mobile Tabs --}}
<div class="subpage-tabs">
@include('web.partials.subpage-sidenav', [
'items' => $items,
'active' => $activeKey,
'mode' => 'tabs'
])
</div>
{{-- Desktop Side --}}
<aside class="subpage-side" aria-label="마이페이지 메뉴">
@include('web.partials.subpage-sidenav', [
'items' => $items,
'active' => $activeKey,
'mode' => 'side'
])
</aside>

View File

@ -0,0 +1,44 @@
@php
$rawTabs = config('web.policy_tabs', []);
$items = collect($rawTabs)->map(function ($t) {
$url = '#';
if (!empty($t['route']) && \Illuminate\Support\Facades\Route::has($t['route'])) {
$url = route($t['route']);
}
return [
'label' => $t['label'] ?? '',
'url' => $url,
'key' => $t['key'] ?? null,
];
})->values()->all();
$activeKey = $activeKey ?? null;
if (!$activeKey) {
foreach ($rawTabs as $t) {
if (!empty($t['route']) && request()->routeIs($t['route'])) {
$activeKey = $t['key'] ?? null;
break;
}
}
}
@endphp
{{-- Mobile Tabs --}}
<div class="subpage-tabs">
@include('web.partials.subpage-sidenav', [
'items' => $items,
'active' => $activeKey,
'mode' => 'tabs'
])
</div>
{{-- Desktop Side --}}
<aside class="subpage-side" aria-label="약관 및 정책 메뉴">
@include('web.partials.subpage-sidenav', [
'items' => $items,
'active' => $activeKey,
'mode' => 'side'
])
</aside>

View File

@ -0,0 +1,4 @@
<header class="subpage-header">
<h1 class="subpage-title">{{ $title }}</h1>
@if($desc) <p class="subpage-desc">{{ $desc }}</p> @endif
</header>

View File

@ -0,0 +1,29 @@
@props(['items' => [], 'active' => null, 'mode' => 'side'])
@if($mode === 'tabs')
<nav class="subnav subnav--tabs" aria-label="서브메뉴 탭">
@foreach($items as $it)
@php $isActive = ($active && $active === ($it['key'] ?? $it['url'])); @endphp
<a href="{{ $it['url'] }}"
class="subnav-tab {{ $isActive ? 'is-active' : '' }}"
@if($isActive) aria-current="page" @endif>
{{ $it['label'] }}
</a>
@endforeach
</nav>
@else
<nav class="subnav subnav--side" aria-label="서브메뉴">
<ul class="subnav-list">
@foreach($items as $it)
@php $isActive = ($active && $active === ($it['key'] ?? $it['url'])); @endphp
<li>
<a href="{{ $it['url'] }}"
class="subnav-link {{ $isActive ? 'is-active' : '' }}"
@if($isActive) aria-current="page" @endif>
{{ $it['label'] }}
</a>
</li>
@endforeach
</ul>
</nav>
@endif

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '이메일무단수집거부';
$pageDesc = '웹사이트 내 이메일 주소의 무단 수집을 거부합니다.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '약관 및 정책', 'url' => url('/policy')],
['label' => '이메일무단수집거부', 'url' => url()->current()],
];
$policyActive = 'email';
@endphp
@extends('web.layouts.subpage')
@section('title', '이메일무단수집거부 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 이메일무단수집거부 안내입니다. 무단 수집 및 이용에 대한 조치를 안내합니다.')
@section('canonical', url('/policy/email'))
@section('subcontent')
<div class="policy-email-page">
@include('web.partials.content-head', [
'title' => '무단 수집 금지 안내',
'desc' => '자동 수집 프로그램 등을 통한 이메일 주소 수집을 금지합니다.'
])
{{-- TODO: 이메일무단수집거부 본문 --}}
</div>
@endsection

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '이용약관';
$pageDesc = '서비스 이용 조건, 책임 범위, 분쟁 처리 기준 안내.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '약관 및 정책', 'url' => url('/policy')],
['label' => '이용약관', 'url' => url()->current()],
];
$policyActive = 'terms';
@endphp
@extends('web.layouts.subpage')
@section('title', '이용약관 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 이용약관입니다. 서비스 이용 조건과 책임 범위, 분쟁 처리 기준을 안내합니다.')
@section('canonical', url('/policy/terms'))
@section('subcontent')
<div class="policy-terms-page">
@include('web.partials.content-head', [
'title' => '서비스 이용 규칙',
'desc' => '회원 의무, 거래/결제, 환불, 제재 기준을 포함합니다.'
])
{{-- TODO: 이용약관 본문 --}}
</div>
@endsection

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '개인정보처리방침';
$pageDesc = '개인정보 수집/이용/보관/파기 및 이용자 권리 안내.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '약관 및 정책', 'url' => url('/policy')],
['label' => '개인정보처리방침', 'url' => url()->current()],
];
$policyActive = 'privacy';
@endphp
@extends('web.layouts.subpage')
@section('title', '개인정보처리방침 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 개인정보처리방침입니다. 개인정보 수집/이용/보관/파기 및 이용자 권리를 안내합니다.')
@section('canonical', url('/policy/privacy'))
@section('subcontent')
<div class="policy-privacy-page">
@include('web.partials.content-head', [
'title' => '개인정보 처리 기준',
'desc' => '수집 목적, 보관 기간, 제공/위탁, 이용자 권리 내용을 포함합니다.'
])
{{-- TODO: 개인정보처리방침 본문 --}}
</div>
@endsection

View File

@ -0,0 +1,29 @@
@php
$pageTitle = '개인정보처리방침';
$pageDesc = '개인정보 수집/이용/보관/파기 및 이용자 권리 안내.';
$breadcrumbs = [
['label' => '홈', 'url' => url('/')],
['label' => '약관 및 정책', 'url' => url('/policy')],
['label' => '개인정보처리방침', 'url' => url()->current()],
];
$policyActive = 'privacy';
@endphp
@extends('web.layouts.subpage')
@section('title', '개인정보처리방침 | PIN FOR YOU')
@section('meta_description', 'PIN FOR YOU 개인정보처리방침입니다. 개인정보 수집/이용/보관/파기 및 이용자 권리를 안내합니다.')
@section('canonical', url('/policy/privacy'))
@section('subcontent')
<div class="policy-privacy-page">
@include('web.partials.content-head', [
'title' => '개인정보 처리 기준',
'desc' => '수집 목적, 보관 기간, 제공/위탁, 이용자 권리 내용을 포함합니다.'
])
{{-- TODO: 개인정보처리방침 본문 --}}
</div>
@endsection

View File

@ -7,4 +7,26 @@ Route::get('/', function () {
Route::view('/', 'web.home')->name('web.home'); Route::view('/', 'web.home')->name('web.home');
Route::prefix('cs')->name('web.cs.')->group(function () {
Route::view('/notice', 'web.cs.notice.index')->name('notice.index');
Route::view('/faq', 'web.cs.faq.index')->name('faq.index');
Route::view('/kakao', 'web.cs.kakao.index')->name('kakao.index');
Route::view('/qna', 'web.cs.qna.index')->name('qna.index');
Route::view('/guide', 'web.cs.guide.index')->name('guide.index');
});
Route::prefix('mypage')->name('web.mypage.')->group(function () {
Route::view('/info', 'web.mypage.info.index')->name('info.index');
Route::view('/usage', 'web.mypage.usage.index')->name('usage.index');
Route::view('/exchange', 'web.mypage.exchange.index')->name('exchange.index');
Route::view('/qna', 'web.mypage.qna.index')->name('qna.index');
});
Route::prefix('policy')->name('web.policy.')->group(function () {
Route::view('/', 'web.policy.index')->name('index');
Route::view('/privacy', 'web.policy.privacy.index')->name('privacy.index');
Route::view('/terms', 'web.policy.terms.index')->name('terms.index');
Route::view('/email', 'web.policy.email.index')->name('email.index');
});
Route::fallback(fn () => abort(404)); Route::fallback(fn () => abort(404));