This commit is contained in:
2026-02-06 16:09:02 +09:00
parent 1a1fa178e0
commit a231d024b5
2 changed files with 32 additions and 32 deletions

62
App.tsx
View File

@@ -394,16 +394,16 @@ const App: React.FC = () => {
</div>
{(selectedHotspot || isAdding) && (
<div className="z-[2000] absolute bottom-0 left-0 right-0 md:top-0 md:left-auto md:w-[480px] bg-white shadow-2xl md:h-full rounded-t-[40px] md:rounded-none overflow-hidden border-l border-gray-100 flex flex-col animate-in slide-in-from-right-8 duration-500">
<div className="p-8 flex flex-col h-full overflow-y-auto custom-scrollbar">
<div className="flex justify-between items-center mb-10">
<h2 className="text-3xl font-black text-slate-900 tracking-tighter">{isAdding ? "새 WiFi 등록" : "장소 정보"}</h2>
<div className="z-[2000] absolute bottom-0 left-0 right-0 md:top-0 md:left-auto md:w-[480px] bg-white shadow-2xl h-auto max-h-[90vh] md:h-full rounded-t-[40px] md:rounded-none overflow-hidden border-l border-gray-100 flex flex-col animate-in slide-in-from-right-8 duration-500">
<div className="p-5 md:p-8 flex flex-col h-full overflow-y-auto custom-scrollbar">
<div className="flex justify-between items-center mb-6 md:mb-10">
<h2 className="text-2xl md:text-3xl font-black text-slate-900 tracking-tighter">{isAdding ? "새 WiFi 등록" : "장소 정보"}</h2>
<button onClick={() => { setIsAdding(false); setSelectedHotspot(null); setNewHotspotPos(null); }} className="p-2 hover:bg-slate-100 rounded-full text-slate-400"><X size={28} /></button>
</div>
{isAdding ? (
<form onSubmit={saveWiFi} className="space-y-8">
<div className="p-6 bg-orange-50 rounded-[32px] border border-orange-100 flex items-center gap-4 shadow-sm">
<form onSubmit={saveWiFi} className="space-y-6 md:space-y-8">
<div className="p-4 md:p-6 bg-orange-50 rounded-[24px] md:rounded-[32px] border border-orange-100 flex items-center gap-4 shadow-sm">
<MapPin className="text-orange-500" />
<div>
<p className="text-[10px] font-black text-orange-400 uppercase"> </p>
@@ -411,58 +411,58 @@ const App: React.FC = () => {
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-3 gap-2 md:gap-4">
{(['general', 'cafe', 'restaurant'] as const).map(type => (
<button key={type} type="button" onClick={() => setSelectedIconType(type)} className={`flex flex-col items-center py-6 px-2 rounded-[24px] border-4 transition-all gap-2 ${selectedIconType === type ? 'border-blue-600 bg-blue-50 text-blue-700 shadow-md' : 'border-slate-50 bg-slate-50 text-slate-300 hover:bg-slate-100'}`}>
<button key={type} type="button" onClick={() => setSelectedIconType(type)} className={`flex flex-col items-center py-4 md:py-6 px-1 md:px-2 rounded-[16px] md:rounded-[24px] border-4 transition-all gap-2 ${selectedIconType === type ? 'border-blue-600 bg-blue-50 text-blue-700 shadow-md' : 'border-slate-50 bg-slate-50 text-slate-300 hover:bg-slate-100'}`}>
{type === 'cafe' ? <Coffee /> : type === 'restaurant' ? <Utensils /> : <Wifi />}
<span className="text-[10px] font-black uppercase">{type === 'cafe' ? '카페' : type === 'restaurant' ? '음식점' : '일반'}</span>
</button>
))}
</div>
<div className="space-y-4">
<input required name="name" value={formPlaceName} onChange={(e) => setFormPlaceName(e.target.value)} placeholder="장소 명칭 (예: 파스쿠찌)" className="w-full p-5 rounded-2xl bg-slate-50 font-bold outline-none border-2 border-transparent focus:border-blue-500 transition-all shadow-inner" />
<input required name="ssid" placeholder="WiFi 이름(SSID)" className="w-full p-5 rounded-2xl bg-slate-50 font-bold outline-none border-2 border-transparent focus:border-blue-500 transition-all shadow-inner" />
<input name="password" placeholder="비밀번호" className="w-full p-5 rounded-2xl bg-slate-50 font-bold outline-none border-2 border-transparent focus:border-blue-500 transition-all shadow-inner" />
<select name="security" className="w-full p-5 rounded-2xl bg-slate-50 font-black outline-none border-2 border-transparent focus:border-blue-500 shadow-inner">
<div className="space-y-3 md:space-y-4">
<input required name="name" value={formPlaceName} onChange={(e) => setFormPlaceName(e.target.value)} placeholder="장소 명칭 (예: 파스쿠찌)" className="w-full p-4 md:p-5 rounded-2xl bg-slate-50 font-bold outline-none border-2 border-transparent focus:border-blue-500 transition-all shadow-inner" />
<input required name="ssid" placeholder="WiFi 이름(SSID)" className="w-full p-4 md:p-5 rounded-2xl bg-slate-50 font-bold outline-none border-2 border-transparent focus:border-blue-500 transition-all shadow-inner" />
<input name="password" placeholder="비밀번호" className="w-full p-4 md:p-5 rounded-2xl bg-slate-50 font-bold outline-none border-2 border-transparent focus:border-blue-500 transition-all shadow-inner" />
<select name="security" className="w-full p-4 md:p-5 rounded-2xl bg-slate-50 font-black outline-none border-2 border-transparent focus:border-blue-500 shadow-inner">
<option value="WPA2">WPA2 ()</option>
<option value="WPA3">WPA3 ()</option>
<option value="Open"> (Open)</option>
</select>
</div>
<button type="submit" className="w-full py-6 bg-blue-600 hover:bg-blue-700 text-white font-black rounded-3xl shadow-xl active:scale-[0.98] transition-all">WiFi </button>
<button type="submit" className="w-full py-4 md:py-6 bg-blue-600 hover:bg-blue-700 text-white font-black rounded-3xl shadow-xl active:scale-[0.98] transition-all">WiFi </button>
</form>
) : selectedHotspot && (
<div className="space-y-10 animate-in fade-in duration-500">
<div className="p-8 bg-gradient-to-br from-blue-600 to-blue-400 rounded-[40px] flex flex-col items-center text-center space-y-4 shadow-xl relative overflow-hidden">
<div className="p-6 bg-white rounded-3xl shadow-xl z-10 transition-transform hover:scale-110">
{selectedHotspot.iconType === 'cafe' ? <Coffee size={48} className="text-blue-600" /> : selectedHotspot.iconType === 'restaurant' ? <Utensils size={48} className="text-blue-600" /> : <Wifi size={48} className="text-blue-600" />}
<div className="space-y-6 md:space-y-10 animate-in fade-in duration-500">
<div className="p-6 md:p-8 bg-gradient-to-br from-blue-600 to-blue-400 rounded-[24px] md:rounded-[40px] flex flex-col items-center text-center space-y-3 md:space-y-4 shadow-xl relative overflow-hidden">
<div className="p-4 md:p-6 bg-white rounded-2xl md:rounded-3xl shadow-xl z-10 transition-transform hover:scale-110">
{selectedHotspot.iconType === 'cafe' ? <Coffee className="w-8 h-8 md:w-12 md:h-12 text-blue-600" /> : selectedHotspot.iconType === 'restaurant' ? <Utensils className="w-8 h-8 md:w-12 md:h-12 text-blue-600" /> : <Wifi className="w-8 h-8 md:w-12 md:h-12 text-blue-600" />}
</div>
<div className="z-10">
<h3 className="text-4xl font-black text-white tracking-tighter mb-1 break-all">{selectedHotspot.name}</h3>
<p className="text-xs font-black text-white/80 uppercase tracking-widest">{selectedHotspot.ssid}</p>
<h3 className="text-2xl md:text-4xl font-black text-white tracking-tighter mb-1 break-all">{selectedHotspot.name}</h3>
<p className="text-[10px] md:text-xs font-black text-white/80 uppercase tracking-widest">{selectedHotspot.ssid}</p>
</div>
</div>
<div className="p-7 bg-slate-900 rounded-[32px] flex flex-col gap-4 shadow-xl">
<p className="text-[11px] font-black text-slate-500 uppercase tracking-widest">WiFi </p>
<div className="p-5 md:p-7 bg-slate-900 rounded-[24px] md:rounded-[32px] flex flex-col gap-3 md:gap-4 shadow-xl">
<p className="text-[10px] md:text-[11px] font-black text-slate-500 uppercase tracking-widest">WiFi </p>
<div className="flex justify-between items-center">
<p className="text-4xl font-mono font-black text-white tracking-widest overflow-hidden truncate mr-4">{showPass === selectedHotspot.id ? (selectedHotspot.password || 'OPEN') : '••••••••'}</p>
<button onClick={() => setShowPass(showPass === selectedHotspot.id ? null : selectedHotspot.id)} className="p-4 bg-white/10 hover:bg-white/20 text-white rounded-2xl transition-all">{showPass === selectedHotspot.id ? <EyeOff /> : <Eye />}</button>
<p className="text-2xl md:text-4xl font-mono font-black text-white tracking-widest overflow-hidden truncate mr-4">{showPass === selectedHotspot.id ? (selectedHotspot.password || 'OPEN') : '••••••••'}</p>
<button onClick={() => setShowPass(showPass === selectedHotspot.id ? null : selectedHotspot.id)} className="p-3 md:p-4 bg-white/10 hover:bg-white/20 text-white rounded-2xl transition-all">{showPass === selectedHotspot.id ? <EyeOff /> : <Eye />}</button>
</div>
</div>
<div className="p-8 bg-white rounded-[40px] border-4 border-blue-50 flex flex-col items-center gap-6 shadow-sm">
<p className="text-xs font-black text-slate-400 uppercase tracking-widest"> QR </p>
<div className="p-6 bg-white rounded-[40px] shadow-2xl border border-slate-100 transition-all hover:scale-105">
<QRCodeCanvas key={selectedHotspot.id} value={getWifiQRValue(selectedHotspot)} size={240} level="H" includeMargin={false} />
<div className="p-6 md:p-8 bg-white rounded-[24px] md:rounded-[40px] border-4 border-blue-50 flex flex-col items-center gap-4 md:gap-6 shadow-sm">
<p className="text-[10px] md:text-xs font-black text-slate-400 uppercase tracking-widest"> QR </p>
<div className="p-4 md:p-6 bg-white rounded-[24px] md:rounded-[40px] shadow-2xl border border-slate-100 transition-all hover:scale-105">
<QRCodeCanvas key={selectedHotspot.id} value={getWifiQRValue(selectedHotspot)} size={window.innerWidth < 768 ? 160 : 220} level="H" includeMargin={false} />
</div>
<div className="flex items-center gap-2 text-green-600 font-black uppercase text-[10px] tracking-tight"><CheckCircle2 size={16} /> </div>
</div>
<div className="flex flex-col gap-4 pt-6">
<button onClick={() => { if(selectedHotspot.password) { navigator.clipboard.writeText(selectedHotspot.password); alert("비밀번호가 복사되었습니다!"); } }} className="w-full py-6 bg-slate-900 hover:bg-black text-white font-black rounded-3xl shadow-xl transition-all"> </button>
<button onClick={async () => { if(confirm("삭제하시겠습니까?")) { await apiService.deleteHotspot(selectedHotspot.id); setHotspots(await apiService.getHotspots()); setSelectedHotspot(null); } }} className="w-full py-5 text-red-500 hover:bg-red-50 font-black rounded-3xl transition-colors flex items-center justify-center gap-2">
<div className="flex flex-col gap-3 md:gap-4 pt-4 md:pt-6">
<button onClick={() => { if(selectedHotspot.password) { navigator.clipboard.writeText(selectedHotspot.password); alert("비밀번호가 복사되었습니다!"); } }} className="w-full py-4 md:py-6 bg-slate-900 hover:bg-black text-white font-black rounded-3xl shadow-xl transition-all"> </button>
<button onClick={async () => { if(confirm("삭제하시겠습니까?")) { await apiService.deleteHotspot(selectedHotspot.id); setHotspots(await apiService.getHotspots()); setSelectedHotspot(null); } }} className="w-full py-4 text-red-500 hover:bg-red-50 font-black rounded-3xl transition-colors flex items-center justify-center gap-2">
<Trash2 size={20} />
</button>
</div>

View File

@@ -1,7 +1,7 @@
import { WiFiHotspot } from '../types';
const API_Base_URL = 'http://localhost:3000/api/markers';
const API_Base_URL = '/api/markers';
export const apiService = {
getHotspots: async (): Promise<WiFiHotspot[]> => {