diff --git a/closet/migrations/0001_initial.py b/closet/migrations/0001_initial.py index b15629d..b70623d 100644 --- a/closet/migrations/0001_initial.py +++ b/closet/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.5 on 2025-02-08 07:32 +# Generated by Django 5.1.5 on 2025-02-09 06:03 from django.db import migrations, models diff --git a/closet/migrations/0002_initial.py b/closet/migrations/0002_initial.py index 38a7f48..5b00813 100644 --- a/closet/migrations/0002_initial.py +++ b/closet/migrations/0002_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.5 on 2025-02-08 07:32 +# Generated by Django 5.1.5 on 2025-02-09 06:03 import django.db.models.deletion from django.conf import settings diff --git a/closet/urls.py b/closet/urls.py index 4cfc138..b2d0f25 100644 --- a/closet/urls.py +++ b/closet/urls.py @@ -33,6 +33,10 @@ path('upload-history/', views.upload_history, name="upload_history"), + path('mycloset/', views.mycloset_view, name="mycloset_view"), + path("mycloset/category//", views.category_detail_view, name="category_detail"), + + ] diff --git a/closet/views.py b/closet/views.py index f200765..d4c8bf9 100644 --- a/closet/views.py +++ b/closet/views.py @@ -1176,3 +1176,54 @@ def generate_cody_recommendation(request): # image_url = request.session.get("uploaded_image_url", None) # return render(request, 'closet/test_image_result.html', {"image_url": image_url}) + + + +#나만의 옷장 카테고리별 분류 +@login_required +def mycloset_view(request): + user = request.user + categories = UserCategory.objects.filter(user_id=user.id) + + category_data = [] + + for category in categories: + outfits = ( + MyCloset.objects.filter(user_id=user.id, user_category_id=category.id) + .select_related("outfit") + .order_by("created_at")[:3] + ) + images = [outfit.outfit.image.url if outfit.outfit and outfit.outfit.image else "/static/images/mycloset/default.jpg" for outfit in outfits] + + while len(images) < 3: + images.append("/static/images/mycloset/mycloset_background.svg") + + category_data.append( + { + "category_id": category.id, + "category_name": category.name, + "images": images, + } + ) + + return render(request, "closet/mycloset/mycloset.html", {"categories": category_data}) + + + + +def category_detail_view(request, category_id): + user = request.user + category = get_object_or_404(UserCategory, id=category_id, user_id=user.id) + + outfits = MyCloset.objects.filter(user_id=user.id, user_category_id=category_id).select_related("outfit") + + items = [ + { + "id": outfit.id, + "image": outfit.outfit.image.url if outfit.outfit and outfit.outfit.image else "/static/images/mycloset/default.jpg", + "created_at": outfit.created_at.strftime("%Y-%m-%d %H:%M:%S"), + } + for outfit in outfits + ] + + return render(request, "closet/mycloset/mycloset_category_detail.html", {"category_name": category.name, "items": items}) diff --git a/config/sitemaps.py b/config/sitemaps.py index cc803f5..e603eee 100644 --- a/config/sitemaps.py +++ b/config/sitemaps.py @@ -43,4 +43,4 @@ def location(self, obj): sitemaps = { 'static': StaticViewSitemap, 'outfits': OutfitSitemap, -} +} \ No newline at end of file diff --git a/static/base.css b/static/base.css index fe60f76..7aa9147 100644 --- a/static/base.css +++ b/static/base.css @@ -1,122 +1,123 @@ main { - width: 90%; - padding-bottom: 4rem; - /* bottom-nav 높이만큼 여백 */ - margin: 0 auto; - /* 가운데 정렬 */ - box-sizing: border-box; - min-height: 100vh; - overflow-y: auto; - /* 세로 스크롤 허용 */ - overflow-x: hidden; - /* 가로 스크롤 방지 */ - scrollbar-width: none; - /* Firefox에서 스크롤바 숨김 */ - padding-block: 4rem; + width: 100%; + padding-inline: 0; + padding-bottom: 4rem; + /* bottom-nav 높이만큼 여백 */ + margin: 0 auto; + /* 가운데 정렬 */ + box-sizing: border-box; + min-height: 100vh; + overflow-y: auto; + /* 세로 스크롤 허용 */ + overflow-x: hidden; + /* 가로 스크롤 방지 */ + scrollbar-width: none; + /* Firefox에서 스크롤바 숨김 */ + padding-block: 4rem; } /* Chrome, Safari, Edge에서 스크롤바 숨기기 */ main::-webkit-scrollbar { - display: none; + display: none; } header { - background-image: url('/static/images/top-nav-background.svg'); - background-size: cover; - /* 배경 이미지가 요소를 덮도록 설정 */ - background-position: center; - /* 배경 이미지가 중앙 정렬되도록 설정 */ - box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); - position: fixed; - top: 0; - z-index: 10; - width: 100%; - max-width: 600px; + background-image: url("/static/images/top-nav-background.svg"); + background-size: cover; + /* 배경 이미지가 요소를 덮도록 설정 */ + background-position: center; + /* 배경 이미지가 중앙 정렬되도록 설정 */ + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + position: fixed; + top: 0; + z-index: 10; + width: 100%; + max-width: 600px; } nav { - height: 4rem; - padding-inline: 1rem; - display: flex; - align-items: center; - justify-content: space-between; + height: 4rem; + padding-inline: 1rem; + display: flex; + align-items: center; + justify-content: space-between; } nav a img { - height: 2rem; + height: 2rem; } ul { - display: flex; - align-items: center; + display: flex; + align-items: center; } .logout { - margin-bottom: 8px; - color: #b2b2b2; - font-family: Inter; - font-size: 0.9375rem; - font-style: normal; - font-weight: 600; - line-height: 180%; - /* 1.6875rem */ + margin-bottom: 8px; + color: #b2b2b2; + font-family: Inter; + font-size: 0.9375rem; + font-style: normal; + font-weight: 600; + line-height: 180%; + /* 1.6875rem */ } ul > a > img { - padding-inline: 0.5rem; + padding-inline: 0.5rem; } .bottom-nav { - position: fixed; - bottom: 0; - z-index: 10; - width: 100%; - max-width: 600px; + position: fixed; + bottom: 0; + z-index: 10; + width: 100%; + max-width: 600px; } .bottomNav { - height: 3.4375rem; /* 55px */ - z-index: 10; - background-color: white; - display: flex; - justify-content: space-between; - align-items: center; - padding-inline: 3rem; - border-top: 1px solid var(--color-gray-very-light); + height: 3.4375rem; /* 55px */ + z-index: 10; + background-color: white; + display: flex; + justify-content: space-between; + align-items: center; + padding-inline: 3rem; + border-top: 1px solid var(--color-gray-very-light); } /* 네비게이션 아이콘 크기 조정 */ .nav-icon { - width: 1.5625rem; /* 25px */ - height: auto; + width: 1.5625rem; /* 25px */ + height: auto; } .nav-icon.large { - width: 2.5rem; /* 40px */ - height: auto; + width: 2.5rem; /* 40px */ + height: auto; } /* 현재 페이지 아이콘 스타일 */ .nav-icon.active { - filter: brightness(0); /* 이미지를 검정색으로 변경 */ + filter: brightness(0); /* 이미지를 검정색으로 변경 */ } .bug-report { - bottom: 3.4375rem; /* 55px */ - position: fixed; - right: 20px; - /* bottom-nav 높이만큼 띄움 */ - z-index: 9; - /* bottom-nav보다 낮은 z-index */ - padding: 6px 10px; - font-size: 11px; - background-color: rgba(155, 27, 27, 0.8); - /* 약간 투명하게 */ - color: white; - text-decoration: none; - border-radius: 4px; - font-weight: 500; - transition: all 0.3s ease; - opacity: 0.7; - /* 기본 상태는 약간 투명하게 */ + bottom: 3.4375rem; /* 55px */ + position: fixed; + right: 20px; + /* bottom-nav 높이만큼 띄움 */ + z-index: 9; + /* bottom-nav보다 낮은 z-index */ + padding: 6px 10px; + font-size: 11px; + background-color: rgba(155, 27, 27, 0.8); + /* 약간 투명하게 */ + color: white; + text-decoration: none; + border-radius: 4px; + font-weight: 500; + transition: all 0.3s ease; + opacity: 0.7; + /* 기본 상태는 약간 투명하게 */ } .bug-report:hover { - opacity: 1; - /* 호버 시 완전 불투명하게 */ - background-color: rgb(155, 27, 27); + opacity: 1; + /* 호버 시 완전 불투명하게 */ + background-color: rgb(155, 27, 27); } diff --git a/static/closet/css/mycloset.css b/static/closet/css/mycloset.css new file mode 100644 index 0000000..2889bb1 --- /dev/null +++ b/static/closet/css/mycloset.css @@ -0,0 +1,118 @@ +.category-container { + display: grid; + grid-template-columns: 1fr 1fr; + grid-auto-rows: 33rem; /* 고정된 기본 높이 */ + align-items: stretch; /* 내부 요소 크기에 맞춰 자동 조정 */ + place-items: center; +} + +@media (max-width: 517px) { + .category-container { + grid-auto-rows: 28rem; + } +} +@media (max-width: 470px) { + .category-container { + grid-auto-rows: 25rem; + } +} +@media (max-width: 380px) { + .category-container { + grid-auto-rows: 22rem; + } +} +@media (max-width: 348px) { + .category-container { + grid-auto-rows: 20rem; + } +} + +.category { + width: 88%; + max-height: 90%; + border-radius: 120px; + text-align: center; + color: white; + font-size: 16px; + font-weight: bold; + margin-bottom: 10px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + overflow: hidden; + box-shadow: 3px 6px 4px 0px rgba(0, 0, 0, 0.52); +} +.image-container { + position: relative; + width: 100%; +} +.image-container img { + width: 100%; + object-fit: cover; + aspect-ratio: 1/1.3; +} +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + border-radius: 20px; +} +.image-container .top-image { + width: 100%; + height: 50%; + display: block; +} +.image-container .bottom-images { + display: flex; + height: 50%; +} +.image-container .bottom-images img { + width: 50%; + height: 100%; + object-fit: cover; + aspect-ratio: 1/2; +} +.category span { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 18px; + z-index: 1; +} +.add-category { + width: 88%; + height: 90%; + border-radius: 120px; + background: rgba(227, 227, 227, 0.43); + box-shadow: 3px 6px 4px 0px rgba(0, 0, 0, 0.52); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.add-category span { + display: flex; + align-items: center; + color: #6c5044; + font-family: Inter; + font-size: 1.2rem; + font-style: normal; + font-weight: 600; + margin-inline: 1rem; +} +.add-category span img { + margin-right: 10px; + margin-top: 2px; +} +h1 { + color: #454347; + font-size: 1.25rem; + font-style: normal; + font-weight: 600; + line-height: normal; +} diff --git a/static/closet/css/mycloset_category_detail.css b/static/closet/css/mycloset_category_detail.css new file mode 100644 index 0000000..ddae02a --- /dev/null +++ b/static/closet/css/mycloset_category_detail.css @@ -0,0 +1,41 @@ +.items-container { + display: grid; + grid-template-columns: 1fr 1fr; + place-items: center; + grid-auto-rows: 22rem; /* 고정된 기본 높이 */ + align-items: stretch; /* 내부 요소 크기에 맞춰 자동 조정 */ +} +@media (max-width: 500px) { + .items-container { + grid-auto-rows: 17rem; + } +} +@media (max-width: 440px) { + .items-container { + grid-auto-rows: 16rem; + } +} +@media (max-width: 370px) { + .items-container { + grid-auto-rows: 12rem; + } +} +.item { + width: 88%; + height: 90%; +} +img { + width: 100%; + height: 100%; + border-radius: 1rem; +} +.container > h1:nth-of-type(2) { + text-align: center; +} +h1 { + color: #454347; + font-size: 1.25rem; + font-style: normal; + font-weight: 600; + line-height: normal; +} diff --git a/static/closet/js/analysis.js b/static/closet/js/analysis.js index cda915f..7c2a5c3 100644 --- a/static/closet/js/analysis.js +++ b/static/closet/js/analysis.js @@ -21,8 +21,7 @@ document.querySelector("form").onsubmit = async function (event) { try { await handleFormSubmit(event.target, elements); - } - catch (error) { + } catch (error) { handleError(error, elements); } finally { isUploading = false; @@ -47,16 +46,20 @@ async function handleFormSubmit(form, elements) { const result = await response.json(); console.log(result); analysisResult = result; -// 만약 응답이 { outfit_id, data: { ... } } 형식이면 data 내부를 사용 + // 만약 응답이 { outfit_id, data: { ... } } 형식이면 data 내부를 사용 const analysisData = result.data ? result.data : result; displayFilteredResults(analysisData); if (result.outfit_id) { - document.getElementById("outfit-id-display").textContent = `Outfit ID: ${result.outfit_id}`; - document.getElementById("saveToClosetBtn").setAttribute("data-outfit-id", result.outfit_id); + document.getElementById( + "outfit-id-display" + ).textContent = `Outfit ID: ${result.outfit_id}`; + document + .getElementById("saveToClosetBtn") + .setAttribute("data-outfit-id", result.outfit_id); document.getElementById("saveToClosetBtn").disabled = false; } - + updateUIWithResult(elements); } @@ -68,12 +71,10 @@ function updateUIForUpload(elements) { } function updateUIWithResult(elements, result) { - elements.resultSection.style.display = "block"; elements.uploadControls.classList.add("hidden"); elements.getCodyButton.style.display = "block"; elements.loadingDiv.style.display = "none"; - // ✅ "나만의 옷장에 저장하기" 버튼 표시 const saveButton = document.getElementById("show-category-slide"); @@ -101,9 +102,9 @@ function displayFilteredResults(data) { displayContainer.innerHTML = ""; // 기존 결과 초기화 if (!data || Object.keys(data).length === 0) { - console.warn("분석 데이터가 비어 있습니다:", data); - displayContainer.textContent = "분석 결과가 없습니다."; - return; + console.warn("분석 데이터가 비어 있습니다:", data); + displayContainer.textContent = "분석 결과가 없습니다."; + return; } // ✅ 정보 컨테이너 (제품 설명, 태그, 카테고리 정보) @@ -114,46 +115,49 @@ function displayFilteredResults(data) { const tagsContainer = document.createElement("div"); tagsContainer.classList.add("tags"); if (data.tag && Array.isArray(data.tag)) { - data.tag.forEach(tag => { - const tagElement = document.createElement("a"); - tagElement.href = "#"; - tagElement.textContent = `#${tag}`; - tagElement.classList.add("tag-item"); - tagsContainer.appendChild(tagElement); - }); + data.tag.forEach((tag) => { + const tagElement = document.createElement("a"); + tagElement.href = "#"; + tagElement.textContent = `#${tag}`; + tagElement.classList.add("tag-item"); + tagsContainer.appendChild(tagElement); + }); } // ✅ 카테고리 정보 (태그 아래) const categoryInfo = document.createElement("div"); categoryInfo.classList.add("result-section"); const filteredData = { - "Category": data.category || "없음", - "Fit": data.fit || "없음", - "Season": data.season || "없음", - "Style": data.design_style || "없음", - "Detail": data.detail || "없음", + Category: data.category || "없음", + Fit: data.fit || "없음", + Season: data.season || "없음", + Style: data.design_style || "없음", + Detail: data.detail || "없음", }; Object.entries(filteredData).forEach(([key, value]) => { - const p = document.createElement("p"); - p.innerHTML = `${key}: ${Array.isArray(value) ? value.join(", ") : value}`; - categoryInfo.appendChild(p); + const p = document.createElement("p"); + p.innerHTML = `${key}: ${ + Array.isArray(value) ? value.join(", ") : value + }`; + categoryInfo.appendChild(p); }); - // ✅ Product Comment (독립된 아래 섹션) const productCommentSection = document.createElement("div"); productCommentSection.classList.add("product-comment-section"); const productComment = document.createElement("p"); productComment.classList.add("product-comment"); - productComment.innerHTML = `Product Comment
${data.comment || "제품 설명이 없습니다."}`; + productComment.innerHTML = `Product Comment
${ + data.comment || "제품 설명이 없습니다." + }`; productCommentSection.appendChild(productComment); // ✅ 요소 배치 순서 조정 - infoContainer.appendChild(tagsContainer); // 태그 - infoContainer.appendChild(categoryInfo); // 카테고리 정보 + infoContainer.appendChild(tagsContainer); // 태그 + infoContainer.appendChild(categoryInfo); // 카테고리 정보 displayContainer.appendChild(infoContainer); displayContainer.appendChild(productCommentSection); // Product Comment는 독립된 아래 섹션 diff --git a/static/closet/js/mycloset/category_detail.js b/static/closet/js/mycloset/category_detail.js new file mode 100644 index 0000000..e69de29 diff --git a/static/closet/js/weather.js b/static/closet/js/weather.js new file mode 100644 index 0000000..e8c9ec5 --- /dev/null +++ b/static/closet/js/weather.js @@ -0,0 +1,65 @@ +function success(position) { + const lat = position.coords.latitude; + const lon = position.coords.longitude; + section_fetchWeatherData(lat, lon); +} + +function error() { + console.log("위치 정보를 불러올 수 없어 서울 날씨를 표시합니다."); + section_fetchWeatherData("37.5665", "126.9780"); +} + +function section_fetchWeatherData(lat, lon) { + const weatherElement = document.getElementById("weather"); + + console.log(`🌍 요청 URL: /api/weather/?lat=${lat}&lon=${lon}`); + + fetch(`/api/weather/?lat=${lat}&lon=${lon}`) + .then((response) => { + console.log("📡 응답 상태 코드:", response.status); + return response.json(); + }) + .then((data) => { + console.log("📩 API 응답 데이터:", data); + + if (!data.weather || !data.weather.main) { + console.error("❌ 응답 데이터에 weather 정보가 없음:", data); + weatherElement.innerHTML = `

날씨 정보를 불러올 수 없습니다. (데이터 오류)

`; + return; + } + + console.log("✅ 날씨 데이터 정상 수신"); + displayWeather(data); + }) + .catch((error) => { + console.error("❌ 네트워크 또는 서버 오류:", error); + weatherElement.innerHTML = `

날씨 정보를 불러올 수 없습니다. (네트워크 오류)

`; + }); +} + +function displayWeather(data) { + console.log(data); + const weatherElement = document.getElementById("weather"); + const temperature = data.weather.main.temp; + const humidity = data.weather.main.humidity; + const windSpeed = data.weather.wind.speed; + const weatherDescription = data.weather.weather[0].description; + const iconCode = data.weather.weather[0].icon; + const iconUrl = `https://openweathermap.org/img/wn/${iconCode}@2x.png`; + + weatherElement.innerHTML = ` +
+ ${weatherDescription} + + 온도: ${temperature}°C | 습도: ${humidity}% | 바람: ${windSpeed} m/s
+ 날씨: ${weatherDescription} +
+
+ `; +} + +// 페이지 로드 시 날씨 정보 가져오기 +navigator.geolocation.getCurrentPosition(success, error); diff --git a/static/images/mycloset/mycloset_background.svg b/static/images/mycloset/mycloset_background.svg new file mode 100644 index 0000000..b3d8d07 --- /dev/null +++ b/static/images/mycloset/mycloset_background.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/static/images/mycloset/mycloset_logo.svg b/static/images/mycloset/mycloset_logo.svg new file mode 100644 index 0000000..6cbaa8a --- /dev/null +++ b/static/images/mycloset/mycloset_logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/static/images/mycloset/plusbutton.svg b/static/images/mycloset/plusbutton.svg new file mode 100644 index 0000000..baabb9f --- /dev/null +++ b/static/images/mycloset/plusbutton.svg @@ -0,0 +1,5 @@ + + + + + diff --git "a/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2001.jpg" "b/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2001.jpg" new file mode 100644 index 0000000..5614076 Binary files /dev/null and "b/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2001.jpg" differ diff --git "a/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2002.jpg" "b/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2002.jpg" new file mode 100644 index 0000000..cffbe76 Binary files /dev/null and "b/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2002.jpg" differ diff --git "a/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2003.jpg" "b/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2003.jpg" new file mode 100644 index 0000000..e68f56c Binary files /dev/null and "b/static/images/mycloset/\354\236\204\354\213\234\354\235\264\353\257\270\354\247\2003.jpg" differ diff --git a/static/images/startpage/Group 302.svg b/static/images/startpage/Group 302.svg new file mode 100644 index 0000000..54892a5 --- /dev/null +++ b/static/images/startpage/Group 302.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/static/images/startpage/Group 306.svg b/static/images/startpage/Group 306.svg new file mode 100644 index 0000000..147413f --- /dev/null +++ b/static/images/startpage/Group 306.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/static/images/startpage/Group 307.svg b/static/images/startpage/Group 307.svg new file mode 100644 index 0000000..9b447f4 --- /dev/null +++ b/static/images/startpage/Group 307.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/static/images/startpage/Group 308 (1).svg b/static/images/startpage/Group 308 (1).svg new file mode 100644 index 0000000..a64686d --- /dev/null +++ b/static/images/startpage/Group 308 (1).svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/startpage/downarrow.svg b/static/images/startpage/downarrow.svg new file mode 100644 index 0000000..5902e0c --- /dev/null +++ b/static/images/startpage/downarrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/images/startpage/mainlogo.svg b/static/images/startpage/mainlogo.svg new file mode 100644 index 0000000..9558e64 --- /dev/null +++ b/static/images/startpage/mainlogo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/static/root.css b/static/root.css index 5eb2e18..0c9df14 100644 --- a/static/root.css +++ b/static/root.css @@ -16,6 +16,7 @@ body { align-items: center; width: 600px; background-color: white; + padding: 0; } a { text-decoration: none; /* 밑줄 제거 */ diff --git a/static/user/css/login.css b/static/user/css/login.css index ab590c4..06c3bf5 100644 --- a/static/user/css/login.css +++ b/static/user/css/login.css @@ -1,200 +1,226 @@ /* 상단과 하단 내비바 숨기기 */ .bottom-nav { - display: none !important; + display: none !important; } -nav{ - display: none !important; -} - - -body { - height: 100%; - display: flex; - flex-direction: column; +nav { + display: none !important; } .login-wrapper { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; -} - -/* SEO 텍스트 스타일링 */ -.login-wrapper h1 { - font-size: 28px; - font-weight: 800; - margin: 32px auto 24px; - line-height: 1.4; - word-break: keep-all; - - max-width: 520px; - padding: 0 24px; - text-align: center; -} - -.login-wrapper h2 { - font-size: 16px; - font-weight: 400; - color: var(--color-text-secondary); - line-height: 1.7; - margin-bottom: 32px; - padding: 0 32px; - word-break: keep-all; - max-width: 480px; - margin-left: auto; - margin-right: auto; - text-align: center; + display: flex; + flex-direction: column; + padding-inline: 3rem; } .login-wrapper ul { - list-style: none; - padding: 0; - margin: 0 auto 40px; - max-width: 320px; - display: flex; - flex-direction: column; - gap: 16px; + list-style: none; + padding: 0; + margin: 0 auto 40px; + max-width: 320px; + display: flex; + flex-direction: column; + gap: 16px; } .login-wrapper li { - font-size: 16px; - color: var(--color-text); - line-height: 1.4; - position: relative; - padding-left: 24px; - display: flex; - align-items: center; - transition: transform 0.2s ease; + line-height: 1.4; + position: relative; + padding-left: 24px; + display: flex; + align-items: center; + transition: transform 0.2s ease; } .login-wrapper li::before { - content: ""; - position: absolute; - left: 0; - width: 16px; - height: 16px; - background: black; - border-radius: 50%; - opacity: 0.1; -} - -.login-wrapper li::after { - content: "→"; - position: absolute; - left: 4px; - color: var(--color-primary); - font-size: 12px; + content: ""; + position: absolute; + left: 0; + width: 16px; + height: 16px; + background: black; + border-radius: 50%; + opacity: 0.1; } .login-wrapper h3 { - font-size: 22px; - font-weight: 700; - color: var(--color-text); - margin-bottom: 40px; - line-height: 1.5; - word-break: keep-all; - padding: 0 24px; - text-align: center; - position: relative; -} - -.login-wrapper h3::after { - content: ""; - position: absolute; - bottom: -12px; - left: 50%; - transform: translateX(-50%); - width: 80px; - height: 3px; - background: black; - border-radius: 2px; + font: var(--font-title-1); + word-break: keep-all; + color: #454347; } +.logo { + width: 8.3rem; -.login-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-around; - padding: 48px 20px; /* 상하 여백 추가 */ - box-sizing: border-box; /* padding을 높이에 포함 */ + flex-shrink: 0; } - -h2 { - font-family: var(--font-family); - font-size: 24px; - color: var(--Color1, #454347); - text-align: left; - margin-bottom: 40px; - font-weight: 700; - line-height: 160%; /* 32px */ - margin-left: 32px; +.login-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-around; + padding-top: 13rem; + box-sizing: border-box; /* padding을 높이에 포함 */ } .login-buttons { - display: flex; - flex-direction: column; - gap: 16px; - width: 100%; - max-width: 300px; - + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; + max-width: 300px; } .login-button { - width: 300px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 8px; - font-family: var(--font-family); - cursor: pointer; - background-color: white; - border: 1px solid var(--color-gray-light); - color: var(--color-gray-darkest); - transition: background-color 0.2s; + max-width: 300px; + height: 43px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + cursor: pointer; + border: 1px solid var(--color-gray-light); + color: var(--color-gray-darkest); + transition: background-color 0.2s; +} +.login-button span { + font-family: Inter; + font-size: 0.875rem; + font-style: normal; + font-weight: 600; + line-height: 180%; } #naver-login { - background-color: #03c75a; - color: white; + background-color: #03c75a; + color: white; } #google-login { - display: flex; - align-items: center; - justify-content: center; - background-color: white; - color: black; + display: flex; + align-items: center; + justify-content: center; + background-color: white; + color: black; } #google-login > img { - width: 24px; - height: 24px; - margin-right: 8px; + width: 24px; + height: 24px; + margin-right: 8px; } #naver-login img { - width: 16px; - height: 16px; - margin-right: 8px; + width: 16px; + height: 16px; + margin-right: 8px; } .tryme { - width: 300px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - font-family: var(--font-family); - font-size: var(--font-size-2); - cursor: pointer; - color: var(--color-gray-dark); - text-decoration: underline; - text-underline-position: under; + width: 300px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + color: #a39fa6; + + /* font-semi-title-1 */ + font-family: Inter; + font-size: 0.875rem; + font-style: normal; + font-weight: 600; + line-height: 180%; /* 1.575rem */ + text-decoration: underline; + text-underline-position: under; } .tryme:hover { - color: var(--color-gray-darkest); -} \ No newline at end of file + color: var(--color-gray-darkest); +} + +.arrow { + padding-top: 1rem; + width: 2.5rem; +} + +@keyframes bounce { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } +} + +.arrow { + animation: bounce 1s infinite ease-in-out; +} + +.introduce-container { + background: linear-gradient( + 0deg, + #000 64.97%, + #7b7b7b 78.97%, + #898989 81.97%, + #949494 86.47%, + #fff 99.11% + ); + min-height: 100rem; + width: 100%; + margin-top: 1rem; +} +.top-wrapper { + padding-block: 11rem; +} +h1 { + color: #fff; + font-family: Inter; + font-size: 1.2rem; + font-style: normal; + font-weight: 600; + line-height: 180%; + padding-inline: 2rem; +} +.highlight-font { + color: #6c5044; + font-family: Inter; + font-size: 1.5rem; + font-style: normal; + font-weight: 900; + line-height: 180%; +} +.circle { + margin-top: 3rem; + background: linear-gradient( + 180deg, + #1d1d1d 0%, + #010101 11.97%, + #000 21.98%, + #010101 27.91%, + rgba(54, 57, 37, 0.98) 68.97% + ); + width: 26rem; + height: 26rem; + border-radius: 100%; +} +.text-border-container { + background: linear-gradient( + 270deg, + rgba(0, 0, 0, 0.22) 0%, + rgba(41, 44, 50, 0.4) 2.97%, + rgba(45, 48, 54, 0.85) 18.47%, + rgba(50, 54, 61, 0.89) 32.97%, + #404650 44.72%, + rgba(137, 144, 162, 0.75) 65.42%, + rgba(220, 229, 255, 0.46) 81.57% + ); + max-width: 30.1875rem; + height: 16.47438rem; + border-radius: 7.5rem 0rem 0rem 7.5rem; + transform: translate(8rem, -25rem); + display: flex; + align-items: center; +} +.iphone { + transform: translate(-2rem, -9rem); +} diff --git a/templates/base.html b/templates/base.html index 6364e92..d7f731e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -102,42 +102,65 @@ content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - - + + - - + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - + + - - + + {% block extra_css %}{% endblock extra_css%} + // Chrome으로 리다이렉션 + location.href = `googlechrome://` + cleanURL; + // iOS Safari fallback + setTimeout(function () { + location.href = `safari-https://` + cleanURL; + }, 100); + } + })(); + diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py index 6f58fac..e419d8d 100644 --- a/user/migrations/0001_initial.py +++ b/user/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.5 on 2025-02-08 07:32 +# Generated by Django 5.1.5 on 2025-02-09 10:59 from django.db import migrations, models