first commit

This commit is contained in:
2026-07-19 03:44:35 +09:00
commit 7f950339ae
23281 changed files with 3217138 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.app-layout[data-v-7c7c57d1]{display:flex;width:100vw;height:100vh;overflow:hidden;background:#f8fafc}.main-content[data-v-7c7c57d1]{flex:1;display:flex;flex-direction:column;padding:1.5rem;overflow:hidden}.chat-container[data-v-7c7c57d1]{flex:1;display:flex;flex-direction:column;background:#fff;border-radius:16px;box-shadow:0 10px 25px -5px #0000000d;overflow:hidden;max-width:1000px;margin:0 auto;width:100%}.chat-header[data-v-7c7c57d1]{padding:1.25rem 1.5rem;border-bottom:1px solid #e2e8f0;display:flex;justify-content:space-between;align-items:center;background:#fff}.header-left[data-v-7c7c57d1]{display:flex;align-items:center;gap:12px}.btn-back[data-v-7c7c57d1]{background:none;border:none;color:#64748b;cursor:pointer;padding:4px;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:background .2s}.btn-back[data-v-7c7c57d1]:hover{background:#f1f5f9;color:#1e293b}.chat-header h1[data-v-7c7c57d1]{font-size:1.25rem;font-weight:700;color:#1e293b}.btn-save[data-v-7c7c57d1]{display:flex;align-items:center;gap:8px;padding:8px 16px;background:#f1f5f9;color:#475569;border:none;border-radius:8px;font-size:.9rem;font-weight:600;cursor:pointer;transition:all .2s}.btn-save[data-v-7c7c57d1]:hover{background:#e2e8f0;color:#1e293b}.chat-history[data-v-7c7c57d1]{flex:1;padding:1.5rem;overflow-y:auto;display:flex;flex-direction:column;gap:1.5rem;background:#fdfeff}.message-wrapper[data-v-7c7c57d1]{display:flex;width:100%}.message-wrapper.user[data-v-7c7c57d1]{justify-content:flex-end}.message[data-v-7c7c57d1]{display:flex;gap:12px;max-width:80%}.user .message[data-v-7c7c57d1]{flex-direction:row-reverse}.avatar[data-v-7c7c57d1]{width:36px;height:36px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:.8rem;flex-shrink:0}.assistant .avatar[data-v-7c7c57d1]{background:linear-gradient(135deg,#6366f1,#a855f7);color:#fff}.user .avatar[data-v-7c7c57d1]{background:#e2e8f0;color:#475569}.content[data-v-7c7c57d1]{padding:12px 16px;border-radius:14px;font-size:.95rem;line-height:1.6;white-space:pre-wrap}.assistant .content[data-v-7c7c57d1]{background:#f1f5f9;color:#1e293b;border-top-left-radius:2px}.user .content[data-v-7c7c57d1]{background:#2563eb;color:#fff;border-top-right-radius:2px}.chat-input-section[data-v-7c7c57d1]{padding:1.5rem;border-top:1px solid #e2e8f0;background:#fff}.chat-input-wrapper[data-v-7c7c57d1]{display:flex;gap:12px;align-items:flex-end;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:8px 12px;transition:border-color .2s}.chat-input-wrapper[data-v-7c7c57d1]:focus-within{border-color:#2563eb;background:#fff}textarea[data-v-7c7c57d1]{flex:1;border:none;background:transparent;padding:8px 4px;font-size:1rem;line-height:1.5;resize:none;color:#1e293b;max-height:200px}textarea[data-v-7c7c57d1]:focus{outline:none}.btn-send[data-v-7c7c57d1]{background:#2563eb;color:#fff;border:none;width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;flex-shrink:0;margin-bottom:2px}.btn-send[data-v-7c7c57d1]:hover:not(:disabled){background:#1d4ed8;transform:scale(1.05)}.btn-send[data-v-7c7c57d1]:disabled{background:#cbd5e1;cursor:not-allowed}.input-hint[data-v-7c7c57d1]{margin-top:8px;font-size:.75rem;color:#94a3b8;text-align:center}.typing-indicator[data-v-7c7c57d1]{display:flex;gap:4px;padding:4px 0}.typing-indicator span[data-v-7c7c57d1]{width:6px;height:6px;background:#94a3b8;border-radius:50%;animation:bounce-7c7c57d1 1.4s infinite ease-in-out}.typing-indicator span[data-v-7c7c57d1]:nth-child(1){animation-delay:-.32s}.typing-indicator span[data-v-7c7c57d1]:nth-child(2){animation-delay:-.16s}@keyframes bounce-7c7c57d1{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.chat-history[data-v-7c7c57d1]::-webkit-scrollbar{width:6px}.chat-history[data-v-7c7c57d1]::-webkit-scrollbar-track{background:transparent}.chat-history[data-v-7c7c57d1]::-webkit-scrollbar-thumb{background:#e2e8f0;border-radius:10px}.chat-history[data-v-7c7c57d1]::-webkit-scrollbar-thumb:hover{background:#cbd5e1}
+26
View File
@@ -0,0 +1,26 @@
import{c as x,_ as O,o as D,a as h,b as v,S as V,d as e,u as A,e as $,f as K,F as P,r as j,g as z,w as B,v as L,h as F,i as G,j as g,k as p,l as q,n as H,m as f,p as J,t as N}from"./index-DWIiqzW2.js";/**
* @license lucide-vue-next v0.322.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const U=x("ChevronLeftIcon",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);/**
* @license lucide-vue-next v0.322.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Z=x("SendIcon",[["path",{d:"m22 2-7 20-4-9-9-4Z",key:"1q3vgg"}],["path",{d:"M22 2 11 13",key:"nzbqef"}]]),Q={class:"app-layout"},W={class:"main-content"},X={class:"chat-container"},Y={class:"chat-header"},ee={class:"header-left"},te={class:"message"},se={class:"avatar"},ae={class:"content"},oe={key:0,class:"message-wrapper assistant loading"},ne={class:"chat-input-section"},ie={class:"chat-input-wrapper"},re=["onKeydown"],ce=["disabled"],le={__name:"AIChatView",setup(de){const w=q(),C=p([]),i=p([{role:"assistant",text:"안녕하세요! 비즈니스 분석 및 전략 수립을 도와드리는 AI 어시스턴트입니다. 무엇을 도와드릴까요?"}]),c=p(""),d=p(!1),u=p(null),m=async()=>{try{const s=await g.get("/api/companies");s.ok&&(C.value=await s.json())}catch(s){console.error("Failed to fetch companies:",s)}},b=()=>{localStorage.removeItem("accessToken"),w.push("/login")},k=async()=>{if(!c.value.trim()||d.value)return;const s=c.value,t=localStorage.getItem("ai_preferred_provider")==="gpt"?"gpt":"gemini",n=localStorage.getItem("ai_gemini_key")||"",l=localStorage.getItem("ai_gpt_key")||"";i.value.push({role:"user",text:s}),c.value="",d.value=!0,await I();try{let a="";const M=`
당신은 '강촌상사'의 비즈니스 데이터 관리 전문가입니다.
아래 명령어를 사용하여 시스템을 직접 제어할 수 있습니다.
반드시 답변 마지막 줄에 해당 형식을 포함해 주세요.
1. 업체 이름 변경: [COMMAND] RENAME: 기존이름 -> 새로운이름
2. 새로운 업체 추가: [COMMAND] CREATE: 신규업체명
예: "네, 경주 업체를 새롭게 추가했습니다. [COMMAND] CREATE: 경주"
`;if(t==="gemini"){if(!n)throw new Error("Gemini API Key가 필요합니다.");const o=`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${n}`,r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({contents:[{parts:[{text:M+`
`+s}]}]})});if(!r.ok)throw new Error("Gemini API 오류");a=(await r.json()).candidates[0].content.parts[0].text}else if(t==="gpt"){if(!l)throw new Error("GPT API Key가 필요합니다.");const o=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`},body:JSON.stringify({model:"gpt-4o-mini",messages:[{role:"system",content:M},{role:"user",content:s}]})});if(!o.ok)throw new Error("OpenAI API 오류 (할당량 부족 등)");a=(await o.json()).choices[0].message.content}const _=a.match(/\[COMMAND\] RENAME:\s*(.+)\s*->\s*(.+)/i);if(_){const o=_[1].trim(),r=_[2].trim();(await g.put("/api/reports/rename",{oldName:o,newName:r})).ok&&(a=a.replace(/\[COMMAND\].*$/m,`
✅ 시스템: '${o}' -> '${r}' 변경 완료.`),await m())}const E=a.match(/\[COMMAND\] CREATE:\s*(.+)/i);if(E){const o=E[1].trim();(await g.post("/api/reports/add",{name:o})).ok&&(a=a.replace(/\[COMMAND\].*$/m,`
✅ 시스템: '${o}' 업체 신규 등록 완료.`),await m())}i.value.push({role:"assistant",text:a})}catch(a){console.error("AI Request Failed:",a),i.value.push({role:"assistant",text:`오류: ${a.message}. 설정을 확인해 주세요.`})}finally{d.value=!1,await I()}},S=async()=>{var s;if(i.value.length<=1){alert("저장할 대화 내용이 없습니다.");return}try{const t=((s=i.value.find(l=>l.role==="user"))==null?void 0:s.text.substring(0,20))||"AI 대화";(await g.post("/api/ai/save",{title:t+"...",messages:i.value})).ok?alert("대화가 성공적으로 저장되었습니다."):alert("저장 중 오류가 발생했습니다.")}catch(t){alert("저장 실패: "+t.message)}},I=async()=>{await H(),u.value&&(u.value.scrollTop=u.value.scrollHeight)},T=()=>{w.push("/")},y=()=>{w.push("/")};return D(()=>{m()}),(s,t)=>(f(),h("div",Q,[v(V,{companies:C.value,onSelect:y,onEdit:y,onRefresh:m,onChangeView:y,onLogout:b},null,8,["companies"]),e("main",W,[e("div",X,[e("header",Y,[e("div",ee,[e("button",{class:"btn-back",onClick:T},[v(A(U),{size:20})]),t[1]||(t[1]=e("h1",null,"AI 비즈니스 어시스턴트",-1))]),e("button",{class:"btn-save",onClick:S},[v(A($),{size:18}),t[2]||(t[2]=K(" 저장하기 ",-1))])]),e("div",{class:"chat-history",ref_key:"historyRef",ref:u},[(f(!0),h(P,null,j(i.value,(n,l)=>(f(),h("div",{key:l,class:J(["message-wrapper",n.role])},[e("div",te,[e("div",se,N(n.role==="user"?"U":"AI"),1),e("div",ae,[e("p",null,N(n.text),1)])])],2))),128)),d.value?(f(),h("div",oe,[...t[3]||(t[3]=[e("div",{class:"message"},[e("div",{class:"avatar"},"AI"),e("div",{class:"content"},[e("div",{class:"typing-indicator"},[e("span"),e("span"),e("span")])])],-1)])])):z("",!0)],512),e("div",ne,[e("div",ie,[B(e("textarea",{"onUpdate:modelValue":t[0]||(t[0]=n=>c.value=n),placeholder:"비즈니스 전략이나 분석에 대해 물어보세요... (Shift+Enter 줄바꿈, Enter 전송)",onKeydown:F(G(k,["exact","prevent"]),["enter"]),rows:"1"},null,40,re),[[L,c.value]]),e("button",{class:"btn-send",onClick:k,disabled:d.value||!c.value.trim()},[v(A(Z),{size:20})],8,ce)]),t[4]||(t[4]=e("p",{class:"input-hint"},"Gemini 1.5 Flash 모델이 답변을 생성합니다.",-1))])])])]))}},ue=O(le,[["__scopeId","data-v-7c7c57d1"]]);export{ue as default};
+6
View File
@@ -0,0 +1,6 @@
import{c as r,_ as h,o as p,a as _,b as e,S as m,d as s,u as n,q as u,U as v,s as f,j as g,k,l as y,m as b}from"./index-DWIiqzW2.js";/**
* @license lucide-vue-next v0.322.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const w=r("FolderKanbanIcon",[["path",{d:"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z",key:"1fr9dc"}],["path",{d:"M8 10v4",key:"tgpxqk"}],["path",{d:"M12 10v2",key:"hh53o1"}],["path",{d:"M16 10v6",key:"1d6xys"}]]),S={class:"app-layout"},V={class:"main-content"},x={class:"admin-container"},I={class:"admin-header"},M={class:"header-left"},z={class:"admin-body"},A={class:"admin-grid"},B={class:"admin-card"},C={class:"card-icon"},F={class:"admin-card"},L={class:"card-icon"},j={class:"admin-card"},q={class:"card-icon"},E={__name:"AdminView",setup(H){const i=y(),d=k([]),c=async()=>{try{const o=await g.get("/api/companies");o.ok&&(d.value=await o.json())}catch(o){console.error("Failed to fetch companies:",o)}},l=()=>{localStorage.removeItem("accessToken"),i.push("/login")},t=()=>{i.push("/")};return p(()=>{c()}),(o,a)=>(b(),_("div",S,[e(m,{companies:d.value,onSelect:t,onEdit:t,onRefresh:c,onChangeView:t,onLogout:l},null,8,["companies"]),s("main",V,[s("div",x,[s("header",I,[s("div",M,[e(n(u),{class:"admin-icon",size:24}),a[0]||(a[0]=s("h1",null,"시스템 관리자 센터",-1))])]),s("div",z,[a[4]||(a[4]=s("div",{class:"welcome-card"},[s("h2",null,"안녕하세요, 관리자님"),s("p",null,"이곳은 시스템 설정을 관리하고 등록된 업체 및 데이터를 총괄하는 관리자 전용 공간입니다.")],-1)),s("div",A,[s("div",B,[s("div",C,[e(n(v),{size:24})]),a[1]||(a[1]=s("div",{class:"card-info"},[s("h3",null,"업체 관리"),s("p",null,"등록된 전체 업체 목록을 조회하고 새 업체를 추가하거나 수정합니다.")],-1))]),s("div",F,[s("div",L,[e(n(w),{size:24})]),a[2]||(a[2]=s("div",{class:"card-info"},[s("h3",null,"데이터 동기화"),s("p",null,"각 업체의 크몽 입금 내역 및 기타 리포트 데이터를 관리합니다.")],-1))]),s("div",j,[s("div",q,[e(n(f),{size:24})]),a[3]||(a[3]=s("div",{class:"card-info"},[s("h3",null,"시스템 설정"),s("p",null,"API 엔드포인트 설정 및 데이터 백업 스케줄을 관리합니다.")],-1))])])])])])]))}},N=h(E,[["__scopeId","data-v-b1d73106"]]);export{N as default};
+1
View File
@@ -0,0 +1 @@
.app-layout[data-v-b1d73106]{display:flex;width:100vw;height:100vh;background-color:#0f172a;color:#f8fafc;overflow:hidden}.main-content[data-v-b1d73106]{flex:1;display:flex;flex-direction:column;height:100%;overflow-y:auto;background-color:#0f172a}.admin-container[data-v-b1d73106]{max-width:1200px;width:100%;margin:0 auto;padding:2rem;box-sizing:border-box}.admin-header[data-v-b1d73106]{display:flex;align-items:center;justify-content:space-between;padding-bottom:1.5rem;border-bottom:1px solid rgba(255,255,255,.1);margin-bottom:2rem}.header-left[data-v-b1d73106]{display:flex;align-items:center;gap:12px}.admin-icon[data-v-b1d73106]{color:#6366f1}.admin-header h1[data-v-b1d73106]{font-size:1.5rem;font-weight:700;margin:0}.welcome-card[data-v-b1d73106]{background:linear-gradient(135deg,#6366f126,#a855f70d);border:1px solid rgba(99,102,241,.2);border-radius:12px;padding:2rem;margin-bottom:2rem}.welcome-card h2[data-v-b1d73106]{font-size:1.25rem;margin:0 0 .5rem;color:#fff}.welcome-card p[data-v-b1d73106]{color:#94a3b8;margin:0;font-size:.95rem;line-height:1.5}.admin-grid[data-v-b1d73106]{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:1.5rem}.admin-card[data-v-b1d73106]{background:#1e293b;border:1px solid rgba(255,255,255,.05);border-radius:12px;padding:1.5rem;display:flex;gap:1rem;transition:transform .2s,border-color .2s;cursor:pointer}.admin-card[data-v-b1d73106]:hover{transform:translateY(-2px);border-color:#6366f14d}.card-icon[data-v-b1d73106]{background:#6366f11a;color:#6366f1;width:48px;height:48px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.card-info h3[data-v-b1d73106]{font-size:1.1rem;margin:0 0 .5rem;color:#fff}.card-info p[data-v-b1d73106]{color:#64748b;margin:0;font-size:.875rem;line-height:1.4}@media (max-width: 768px){.admin-container[data-v-b1d73106]{padding:1rem}}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long