<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, viewport-fit=cover">
  <title>에러</title>
  <meta name="appCode" content="UNKNOWN" />
  <link rel="icon" type="image/png" href="/assets/images/favicon-32x32.png">
  <link rel="stylesheet" href="/css/style.css?v=1781687911900" />
  <link rel="stylesheet" href="/css/style_custom.css?v=1781687911900" />
  <script src="/assets/js/tiara.min.js"></script>
  
  
  
  
  
  
  <script src="/js/common.js?v=1781687911900"></script>
  <script src="/js/date.js?v=1781687911900"></script>
  <script src="/js/tiara.js?v=1781687911900"></script>
  <script src="/js/directive.js?v=1781687911900"></script>
  <script src="/js/app-bridge.js?v=1781687911900"></script>
  <script src="/js/file.js?v=1781687911900"></script>
  <script defer src="/assets/js/alpine-3.14.9.min.js"></script>
  <script src="/assets/js/axios-1.12.2.min.js"></script>
  <script>
    const tiara = TiaraTracker.getInstance().init({
      svcDomain: "business.kakaomobility.com",
      sessionTimeout: "1800",
      disableQuery: true,
      deployment: 'production'
    });

    window.trackEvents = window.trackEvents || function() {};

    const appsCodePathRegex = /^(?:\/web|\/api)?\/apps\/([^\/]+)/;
    const webPathRegex = /^\/web\/apps\/([^\/]+)/;
    const webBizprofilePathRegex = /^\/web\/bizprofiles\/([^\/]+)/;
    const webAppBizprofilePathRegex = /^\/web\/apps\/([^\/]+)\/bizprofiles\/([^\/]+)/;

    function getWebAppsCodeBasePath() {
      const path = window.location.pathname;
      const match = path.match(webPathRegex);
      if (match) {
        return `/web/apps/${match[1]}`;
      }
      return '/web';
    }

    function hasAppCodePath() {
        const path = window.location.pathname;
        return appsCodePathRegex.test(path);
    }

    function getAppCode() {
      if (hasAppCodePath()) {
        const path = window.location.pathname;
        const match = path.match(webPathRegex);
        return match[1];
      } else {
        return null
      }
    }

    function getWebBizprofilesExtIdBasePath() {
        const path = window.location.pathname;
        const match = path.match(webBizprofilePathRegex);
        if (match) {
            return `/web/bizprofiles/${match[1]}`;
        }
        return null;
    }

    function getWebAppsCodeBizprofilesExtIdBasePath() {
      const path = window.location.pathname; // 쿼리 파라미터와 해시 제외
      const match = path.match(webAppBizprofilePathRegex);
      if (match) {
        return `/web/apps/${match[1]}/bizprofiles/${match[2]}`;
      }
      return getWebBizprofilesExtIdBasePath();
    }


    function getApiAppsCodeBasePath() {
      const path = window.location.pathname; // 쿼리 파라미터와 해시 제외
      const match = path.match(webPathRegex);
      if (match) {
        return `/api/apps/${match[1]}`;
      }
      return '/api';
    }

    function getApiAppsCodeBizprofilesExtIdBasePath() {
      const path = window.location.pathname; // 쿼리 파라미터와 해시 제외
      const match = path.match(webAppBizprofilePathRegex);
      if (match) {
        return `/api/apps/${match[1]}/bizprofiles/${match[2]}`;
      }
      return getApiBizprofilesExtIdBasePath();
    }

    function getApiBizprofilesExtIdBasePath() {
        const path = window.location.pathname;
        const match = path.match(webBizprofilePathRegex);
        if (match) {
            return `/api/bizprofiles/${match[1]}`;
        }
        return null;
    }

    function getBizprofileExtId() {
      const path = window.location.pathname; // 쿼리 파라미터와 해시 제외
      let match = path.match(webAppBizprofilePathRegex);
      if (match) {
        return match[2] || null;
      }
      match = path.match(webBizprofilePathRegex);
      return match?.[1] || null;
    }

    const webAppsCodeBasePath = getWebAppsCodeBasePath();
    const webAppsCodeBizprofilesExtIdBasePath = getWebAppsCodeBizprofilesExtIdBasePath();
    const apiAppsCodeBasePath = getApiAppsCodeBasePath();
    const apiAppsCodeBizprofilesExtIdBasePath = getApiAppsCodeBizprofilesExtIdBasePath();
    const bizprofileExtId = getBizprofileExtId();

    // Axios 인스턴스 생성
    const apiClient = axios.create({
      timeout: 10000, // 요청 타임아웃 설정
      withCredentials: true, // iframe에서 쿠키 전송 허용
    });

    // 요청 취소를 위한 Map 객체 생성
    const pendingRequests = new Map();

    // 요청 인터셉터
    apiClient.interceptors.request.use(
            (config) => {
              // allowDuplicate 옵션이 true면 중복 차단 로직을 건너뜀
              if (config.allowDuplicate) {
                // loadingBox 옵션이 false가 아니면 로딩 표시
                if (config.loadingBox !== false) {
                  loadingBoxOn();
                }
                return config;
              }

              // 요청 고유 key 생성 (url, method, params, data 등 조합)
              const key = [
                config.method,
                config.url,
                JSON.stringify(config.params || {}),
                JSON.stringify(config.data || {})
              ].join('|');

              if (pendingRequests.has(key)) {
                return Promise.reject(new axios.Cancel('Duplicate request blocked'));
              }
              pendingRequests.set(key, true);
              config.metadata = { requestKey: key };

              // loadingBox 옵션이 false가 아니면 로딩 표시
              if (config.loadingBox !== false) {
                loadingBoxOn();
              }

              return config;
            },
            (error) => {
              return Promise.reject(error);
            }
    );

    apiClient.interceptors.response.use(
            (response) => {
              if (!response.config.allowDuplicate && response.config.metadata) {
                pendingRequests.delete(response.config.metadata.requestKey);
              }

              // loadingBox 옵션이 false가 아니면 로딩 숨김
              if (response.config.loadingBox !== false) {
                loadingBoxOff();
              }

              return response;
            },
            (error) => {
              if (error.config && !error.config.allowDuplicate && error.config.metadata) {
                pendingRequests.delete(error.config.metadata.requestKey);
              }

              // loadingBox 옵션이 false가 아니면 로딩 숨김
              if (error.config && error.config.loadingBox !== false) {
                loadingBoxOff();
              }

              return Promise.reject(error);
            }
    );

    document.addEventListener('DOMContentLoaded', function() {
        const urlParams = new URLSearchParams(window.location.search);
        const returnUrl = urlParams.get('returnUrl');
        if (returnUrl) {
            localStorage.setItem('returnUrl', returnUrl);
        }

        // centered-dialog 모달이 열릴 때 body 스크롤 방지
        const observer = new MutationObserver(() => {
            const hasOpenDialog = document.querySelector('.centered-dialog.open');
            document.body.style.overflow = hasOpenDialog ? 'hidden' : '';
        });
        observer.observe(document.body, {
            attributes: true,
            attributeFilter: ['class'],
            subtree: true
        });

        ShowToast();
    });
  </script>
  
    <meta name="pageType" content="error">
    <script>
    </script>

</head>
<body>
<body>
<div id="wrap-main" class="wrap-main page-error">
    <!-- 헤더 타이틀바 -->
    <!-- @개발 : 인앱 웹뷰 모드에서는 .is-app 클래스 추가 -->
    <div class="header-title-bar">
        <div class="header-bar-util-left">
            <button type="button" class="btn btn-icon btn-close" aria-label="페이지 닫기" onclick="AppBridge.closeWebview();">
                <i class="ic-24-close-black" aria-hidden="true"></i>
            </button>
        </div>
    </div>

    <!-- 콘텐츠 -->
    <div class="error-box">
        <div class="error-img">
            <i class="ic-48-error-red"></i>
        </div>
        <div class="error-title">
            <h2 class="typo-title1-bold typo-neutral1">
                이용에 불편을 드려 죄송합니다
            </h2>
        </div>
        <div class="error-desc">
            <p class="typo-body1">
                페이지를 불러 올 수가 없습니다. 잠시 후 다시 접속해 주세요.
            </p>
        </div>
        <div class="error-actions">
        </div>
    </div>
</div>
</body>
<div
    x-data="Alpine.store('loadingBox')"
    class="loading-box"
    :class="{ 'open': isLoading }"
    aria-live="assertive"
    x-show="isLoading"
    style="display: none;"
>
    <span class="ic-loading" :role="isLoading ? 'alert' : null">
        <i class="ic-48-loading-gray"></i>
        <span class="hidden">로드 중...</span>
    </span>
</div>

<script>
  document.addEventListener('alpine:init', () => {
    Alpine.store('loadingBox', {
      loadingCount: 0,
      get isLoading() { return this.loadingCount > 0; },
      on() { this.loadingCount++; },
      off() { this.loadingCount--; }
    });

    // 전역 함수 등록
    window.loadingBoxOn = function () {
      Alpine.store('loadingBox').on();
    };
    window.loadingBoxOff = function () {
      Alpine.store('loadingBox').off();
    };
  });
</script>

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <script>
        document.addEventListener('alpine:init', () => {

            Alpine.store('toast', {
                message: '',
                type: 'error',
                visible: false,
                timerId: null,

                error(message) {
                    this.type = 'error';
                    this.show(message);
                },

                info(message) {
                    this.type = 'info';
                    this.show(message);
                },

                success(message) {
                    this.type = 'success';
                    this.show(message);
                },

                async show(message) {
                    this.clearTimer();
                    this.visible = false;
                    await sleep(100); // css 로 정의된 animation을 리셋하기 위함

                    this.message = message;
                    this.visible = true;

                    this.timerId = setTimeout(() => {
                        this.visible = false;
                    }, 4400);
                },

                clearTimer() {
                    if (this.timerId) {
                        clearTimeout(this.timerId);
                        this.timerId = null;
                    }
                }

            });

            function sleep(ms) {
                return new Promise(resolve => setTimeout(resolve, ms));
            }
            // 전역 함수 등록
            window.Toast = function (message) {
                Alpine.store('toast').info(message);
            };
            window.ErrorToast = function (message) {
                Alpine.store('toast').error(message);
                /* trackEvents('오류모달토스트노출', { ui_type: 'TOAST', current_page_path: window.location.pathname }); */
            };
            window.CommonErrorToast = function () {
              Alpine.store('toast').error('일시적인 오류입니다. 다시 시도해주세요.');
              /* trackEvents('오류모달토스트노출', { ui_type: 'TOAST', current_page_path: window.location.pathname }); */
            };
            window.SuccessToast = function (message) {
                Alpine.store('toast').success(message);
            };

            window.TemporalErrorToastInit = function (back = false) {
              ToastInit({ message: '일시적인 오류입니다. 다시 시도해주세요.', type: 'info' });
              /* trackEvents('오류모달토스트노출', { ui_type: 'TOAST', current_page_path: window.location.pathname }); */
              if (back) {
                history.back();
              }
            };

            window.ToastInit = function(info) {
                if (!info) {
                    return;
                }
                sessionStorage.setItem('toast', JSON.stringify(info));
            };

            window.ShowToast = function() {
                const info = sessionStorage.getItem('toast');
                if (info) {
                    const toast = JSON.parse(info);
                    const type = toast.type || 'info';

                    if (type === 'error') {
                        ErrorToast(toast.message);
                    } else if (type === 'success') {
                        SuccessToast(toast.message);
                    } else {
                        Toast(toast.message);
                    }

                    sessionStorage.removeItem('toast');
                } else {
                    const url = new URL(window.location.href);
                    const toastMessage = url.searchParams.get('toastMessage');
                    if (toastMessage) {
                        const toastType = url.searchParams.get('toastType') || 'info';

                        if (toastType === 'error') {
                            ErrorToast(toastMessage);
                        } else if (toastType === 'success') {
                            SuccessToast(toastMessage);
                        } else {
                            Toast(toastMessage);
                        }
                        url.searchParams.delete('toastMessage');
                        url.searchParams.delete('toastType');
                        history.replaceState({}, '', `${url.pathname}${url.search}${url.hash}`);
                    }
                }
            };
        });
    </script>

    <div :class="$store.toast.type === 'error' ? 'alert-box' : 'toast-box'" x-data="Alpine.store('toast')" style="bottom: 64px; left: 50%; transform: translateX(-50%);">
        <div
            :class="{
                'toast': $store.toast.type !== 'error',
                'alert error': $store.toast.type === 'error'
             }" :role="$store.toast.type === 'error' ? 'alert' : 'status'" aria-live="assertive" :style="{ display: $store.toast.visible ? 'flex' : 'none' }">
            <p x-html="$store.toast.message"></p>
        </div>
    </div>
</body>
</html>


<link rel="preload" href="/assets/images/ic_48_check_blue.svg" as="image" type="image/svg+xml">
<link rel="preload" href="/assets/images/ic_48_check_circle_on_01.svg" as="image" type="image/svg+xml">
<link rel="preload" href="/assets/images/ic_48_info.svg" as="image" type="image/svg+xml">
<link rel="preload" href="/assets/images/ic_48_warning.svg" as="image" type="image/svg+xml">
<link rel="preload" href="/assets/images/ic_48_warning_red.svg" as="image" type="image/svg+xml">
<link rel="preload" href="/assets/images/ic_48_error_red.svg" as="image" type="image/svg+xml">

<div
  x-data="Alpine.store('commonModal')"
  class="bottom-sheet centered-dialog"
  :class="{ 'open': isVisible }"
  role="dialog"
  aria-modal="true"
  :aria-labelledby="hasTitle ? 'sheet-title' : null"
  data-tiara-skip-impression
  @click.self="$store.commonModal.close()"
>
  <div
   class="bottom-sheet-container"
   :class="context.containerClass"
  >
    <div class="bottom-sheet-head">
      <!-- 네트워크가 끊긴 상태에서 TemporalErrorModal의 waring.svg(경고 아이콘) 을 올바르게 로드하기위해 SVG 직접 작성 -->
      <svg x-show="!context.hideIcon && context.iconType === 'warning'" aria-hidden="true" width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M24 6C33.9411 6 42 14.0589 42 24C42 33.9411 33.9411 42 24 42C14.0589 42 6 33.9411 6 24C6 14.0589 14.0589 6 24 6ZM24 29C22.8954 29 22 29.8954 22 31C22 32.1046 22.8954 33 24 33C25.1046 33 26 32.1046 26 31C26 29.8954 25.1046 29 24 29ZM24 15C22.9457 15 22.0822 15.7788 22.0059 16.7666L22 16.9092L22.2861 25.3633L22.292 25.5049C22.367 26.3425 23.1031 27 24 27C24.9468 27 25.7139 26.267 25.7139 25.3633L26 16.9092L25.9941 16.7666C25.9178 15.7788 25.0543 15 24 15Z" fill="url(#paint0_linear_6225_58723)"/>
        <defs>
          <linearGradient id="paint0_linear_6225_58723" x1="42" y1="42" x2="6" y2="-1.24219" gradientUnits="userSpaceOnUse">
            <stop stop-color="#808597"/>
            <stop offset="1" stop-color="#A4A8B7"/>
          </linearGradient>
        </defs>
      </svg>
      <i :class="iconClass" aria-hidden="true" x-show="!context.hideIcon && context.iconType !== 'warning'"></i>
      <h2 id="sheet-title" class="bottom-sheet-title" x-show="hasTitle" x-html="context.title"></h2>
    </div>

    <div class="bottom-sheet-body" x-show="hasContent && context.contentOverride" x-html="context.content">
    </div>

    <div class="bottom-sheet-body" x-show="hasContent && !context.contentOverride">
      <p class="typo-body1 typo-neutral2" x-show="!context.contentOverride" x-html="context.content"></p>
    </div>

    <div class="bottom-sheet-actions">
      <button type="button" class="btn" :class="closeBtnClass" x-text="closeBtnName" @click="close()"></button>
      <button type="button" class="btn" :class="actionBtnClass" x-show="hasActionBtn" x-text="context.button.action.name" @click="action()"></button>
    </div>
  </div>
</div>

<script>
  document.addEventListener('alpine:init', () => {

    Alpine.store('commonModal', {
      context: {},
      isVisible: false,

      get hasTitle() {
        return this.hasData(this.context.title);
      },

      get iconClass() {
        return `ic-48-${this.context.iconType}`;
      },

      get hasContent() {
        return this.hasData(this.context.content);
      },

      get hasActionBtn() {
        return this.hasData(this.context.button.action.name);
      },

      get closeBtnClass() {
        return `${this.colorClass(this.context.button.close.color)} ${this.context.button.close.class}`;
      },

      get actionBtnClass() {
        return `${this.colorClass(this.context.button.action.color)} ${this.context.button.action.class}`;
      },

      get closeBtnName() {
        const closeBtnName = this.context.button.close.name

        return closeBtnName !== '' ? closeBtnName : (this.hasActionBtn ? '취소' : '확인');
      },

      colorClass(value) {
        return value === 'blue' ? '' : `btn-${value}`;
      },

      hasData(target) {
        return target !== null && target !== '';
      },

      init() {
        this.clear();
      },

      clear() {
        this.context = {
          containerClass: '',
          iconType: 'info', //info, warning, check-blue
          hideIcon: false,
          title: '',
          content: '',
          contentOverride: false,
          button: {
            close: {
              name: '',
              color: 'blue', // blue, gray, red...
              callback: () => {},
              class: ''
            },
            action: {
              name: '',
              color: 'gray',
              callback: () => {},
              class: ''
            }
          },
        }
      },

      openWithOption(options = {}) {
        if (typeof options !== 'object' || options === null) {
          options = {};
        }

        // context 한번에 병합
        if (options) {
          this.deepMerge(this.context, options);
        }

        this.isVisible = true;

        // 모달 노출 자동 추적. dialog 엘리먼트가 재사용되므로 IntersectionObserver 대신 여기서 직접 호출
        const title = (this.context.title || '').replace(/<[^>]+>/g, '').replace(/\s+/g, '').trim();
        if (title && typeof window.trackEvents === 'function') {
          window.trackEvents(`${title}_모달노출`);
        }
      },

      deepMerge(target, source) {
        for (const key in source) {
          if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
            // 객체인 경우 재귀적으로 병합
            if (!target[key] || typeof target[key] !== 'object') {
              target[key] = {};
            }
            this.deepMerge(target[key], source[key]);
          } else {
            // 원시값이나 배열인 경우 직접 할당
            target[key] = source[key];
          }
        }
      },

      close() {
        const callback = this.context.button.close.callback;
        this.isVisible = false;
        this.clear();

        if (callback) {
          callback();
        }
      },

      action() {
        const callback = this.context.button.action.callback;
        this.isVisible = false;
        this.clear();

        if (callback) {
          callback();
        }
      }
    })

    // 전역 함수 등록
    window.Modal = function (title, content) {
      Alpine.store('commonModal').openWithOption({'title': title, 'content': content});
    };
    window.CustomModal = function(options = {}) {
      Alpine.store('commonModal').openWithOption(options);
    }
    window.TemporalErrorModal = function(goBack = false) {
      Alpine.store('commonModal').openWithOption({
                  title: `요청을 처리하지 못했어요.<br />잠시 후 다시 시도해 주세요.`,
                  content: `문제가 계속되면 카카오 T 고객센터로<br />문의해 주세요.`,
                  iconType: 'warning',
                  button: {
                    close: {
                      color: 'gray',
                      ...(goBack && { callback: () => history.back() }),
                    },
                  }
                });
      /* trackEvents('오류모달토스트노출', { ui_type: 'TOAST', current_page_path: window.location.pathname }); */
    }
  });
</script>

<div
        x-data
        x-show="$store.appSelector.isShow"
        x-bind:class="{'open': $store.appSelector.isShow}"
        class="bottom-sheet centered-dialog"
        role="dialog"
        aria-modal="true"
        x-cloak
        @click.self="$store.appSelector.close()"
>
    <div class="bottom-sheet-container bottom-sheet-service-selection">
        <div class="bottom-sheet-header">
            <h2 class="bottom-sheet-title">
                이용할 서비스를 선택해주세요.
            </h2>
        </div>

        <div class="bottom-sheet-body">
            <div class="bottom-sheet-option-btns">
                <template x-for="app in $store.appSelector.apps" :key="app.name">
                    <button type="button" class="btn btn-ghost" @click="$store.appSelector.goTo(app)">
                        <span class="typo-body1 typo-neutral1" x-text="app.name"></span>
                    </button>
                </template>
            </div>
        </div>

        <div class="bottom-sheet-actions" x-show="$store.appSelector.isShowCloseBtn">
            <button type="button" class="btn btn-gray" @click="$store.appSelector.close()">닫기</button>
        </div>
    </div>
</div>


<script>
  document.addEventListener('alpine:init', () => {
    Alpine.store('appSelector', {
      isShow: false,
      isShowCloseBtn: true,
      type: 'web',
      apps: [],

      open(apps, type, isShowCloseBtn) {
        this.apps = apps;
        this.isShow = true;
        this.type = type;
        this.isShowCloseBtn = isShowCloseBtn;
      },
      close() {
        this.isShow = false;
      },
      goTo(app) {
        /* trackEvents(`서비스선택모달${app.name}클릭`, { selected_service_name: app.code }); */
        const apiAppsCodeBasePath = getApiAppsCodeBasePath();
        let url;
        if (hasAppCodePath()) {
          url = `${apiAppsCodeBasePath}/members/update-last-selected-bizprofile`;
        } else {
          url = `${apiAppsCodeBasePath}/apps/${app.code}/members/update-last-selected-bizprofile`;
        }
        apiClient.post(url, new URLSearchParams({bizprofileExtId: app.bizprofileExtId}))
          .then(() => {
            const url = app.url[this.type];
            if (url === 'closeWebview') {
              AppBridge.closeWebview();
            } else {
              window.location.href = url;
            }
          })
          .catch(error => {
            this.close();
            TemporalErrorModal();
          });
      },
    });

    window.AppSelector = function (apps, type = 'web', isShowCloseBtn = true) {
      if (apps.length < 2) {
        const url = apps[0].url[type];
        if (url === 'closeWebview') {
          AppBridge.closeWebview();
        } else {
          window.location.href = url;
        }
        return;
      }
      /* trackEvents(`서비스선택모달노출`); */
      Alpine.store('appSelector').open(apps, type, isShowCloseBtn);
    }
  });
</script>
</body>
</html>
