diff --git a/Authentication/firebase_auth.json b/Authentication/firebase_auth.json new file mode 100644 index 0000000..33db779 --- /dev/null +++ b/Authentication/firebase_auth.json @@ -0,0 +1,10 @@ +{ + "apiKey": "AIzaSyD7kiUugMafggHYlxiwR-E_6_0Rg51GfOk", + "authDomain": "ewhamarket-85d2a.firebaseapp.com", + "databaseURL": "https://ewhamarket-85d2a-default-rtdb.firebaseio.com", + "projectId": "ewhamarket-85d2a", + "storageBucket": "ewhamarket-85d2a.firebasestorage.app", + "messagingSenderId": "90601059988", + "appId": "1:90601059988:web:e8c3d190c6bd2ba1c46681", + "measurementId": "G-TDKXX1YQS9" +} \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..b2e7fb0 --- /dev/null +++ b/app.py @@ -0,0 +1,476 @@ +import logging +from flask import Flask, render_template, request, flash, redirect, url_for, session, jsonify +from database import DBhandler +import os +import hashlib +import sys + +app = Flask(__name__) +app.secret_key = 'super_secret_key' # 세션 관리를 위한 키 설정 + +DB = DBhandler() + +@app.before_request +def set_default_session_values(): + # 세션에 'role' 키가 없을 경우 기본값을 'Seller'로 설정 + if 'role' not in session: + session['role'] = 'Seller' + +# 업로드할 파일의 저장 경로 설정 +UPLOAD_FOLDER = os.path.join('static', 'uploads') +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER + +# 업로드 폴더 생성 +if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER) + +products = {} +users = {} + + +@app.route("/index") +def index(): + return render_template("indexSeller.html", logged_in=('id' in session), user=session.get('nickname')) + +@app.route("/", methods=['GET', 'POST']) +def home(): + if 'id' in session: + if session['role'] == 'seller': + return render_template("homeSeller.html", logged_in=True, user=session.get('nickname')) + return render_template("homeBuyer.html", logged_in=True, user=session.get('nickname')) + return redirect(url_for("login_user")) # 로그인하지 않은 경우 로그인 화면으로 리다이렉트 + + +@app.route("/signUp", methods=['GET', 'POST']) +def sign_up(): + if request.method == "POST": + id = request.form.get("id") + password = request.form.get("password") + password_hash = hashlib.sha256(password.encode('utf-8')).hexdigest() + nickname = request.form.get("nickname") + email = request.form.get("email") + phone = request.form.get("phone") + role = request.form.get("role") + + if DB.insert_user(id, password_hash, nickname, email, phone, role): + return render_template("login.html") + else: + flash("user id already exists!") + return render_template("signUp.html") + + # 사용자 정보를 딕셔너리에 저장 + # users[email] = { + # "id": id, + # "password": password, + # "nickname": nickname, + # "email": email, + # "phone": phone, + # "role": role + # } + # + # if not DB.check_user_exists(email): + # users[email] = { + # "id": id, + # "password": password, + # "nickname": nickname, + # "email": email, + # "phone": phone, + # "role": role + # } + # DB.insert_user(id, email, password, nickname, phone, role) + # session['id'] = id + # return redirect(url_for("home")) + # return render_template("signUp.html", error="이메일이 이미 등록되어 있습니다.") + + # 회원가입 후 세션에 저장하여 자동 로그인 처리 + session['id'] = id + session['nickname'] = nickname + session['role'] = role + + return redirect(url_for("home", logged_in=('id' in session), user=session.get('nickname'))) + + return render_template('signUp.html', logged_in=False) + +# 상품 상세 페이지 +@app.route("/view_detail//") +def product_detail(product_name): + try: + logging.debug(f"Requested product name: {product_name}") + all_products = DB.get_items() + logging.debug(f"All products: {all_products}") + + + product = next((item for item in all_products if item and item.get("name") == product_name), None) + + if not product: + logging.error(f"Product with name '{product_name}' not found.") + return f"Product '{product_name}' not found", 404 + + + role = session.get('role', '').lower() + + if role == 'seller': + return render_template( + "productDetailSeller.html", + product=product, + logged_in=('id' in session), + user=session.get('nickname') + ) + else: + return render_template( + "productDetailBuyer.html", + product=product, + logged_in=('id' in session), + user=session.get('nickname') + ) + + except Exception as e: + logging.error(f"Error retrieving product details: {e}") + return f"An unexpected error occurred: {str(e)}", 500 + +@app.route("/review//") +def product_review_list(product_name): + try: + + all_reviews = DB.get_reviews() + if not all_reviews: + return f"No reviews found for product '{product_name}'", 404 + + + valid_reviews = [review for review in all_reviews if review is not None] + + + product_reviews = [ + review for review in valid_reviews + if review.get("product_name") == product_name + ] + + + product_reviews.sort(key=lambda x: x.get("review_date", ""), reverse=True) + + return render_template( + "ProductreviewsBuyer.html", + product_name=product_name, + reviews=product_reviews, + logged_in=('id' in session), + user=session.get('nickname') + ) + except Exception as e: + logging.error(f"Error retrieving reviews for product '{product_name}': {e}") + return f"An error occurred: {e}", 500 + + + + + +@app.route("/mypage") +def view_review(): + if session['role'] == 'seller': + return render_template("mypageSell.html") + elif session['role'] == 'buyer': + return render_template("mypageBuy.html") + else: + return redirect(url_for("login")) + +# 상품 리스트 +@app.route("/browse", methods=["GET"]) +def browse(): + try: + # Firebase에서 데이터 가져오기 + all_products = DB.get_items() # 리스트로 반환된 데이터 + logging.debug(f"DEBUG: All Products from Firebase: {all_products}") + + # 리스트 형식 확인 및 유효 데이터 필터링 + if isinstance(all_products, list): + valid_products = [product for product in all_products if product is not None] + else: + # 예외 처리: 리스트가 아닌 경우 빈 리스트로 설정 + valid_products = [] + + # 페이지네이션 처리 + page = request.args.get('page', default=1, type=int) + items_per_page = 4 + total_products = len(valid_products) + total_pages = (total_products + items_per_page - 1) // items_per_page + + if page < 1: + page = 1 + elif page > total_pages: + page = total_pages + + start_idx = (page - 1) * items_per_page + end_idx = start_idx + items_per_page + products = valid_products[start_idx:end_idx] + + # 각 제품에 대한 기본 필드 설정 + for product in products: + product["img_path"] = product.get("img_path", "default.jpg") + + return render_template( + "browseBuyer.html", + products=products, + page=page, + total_pages=total_pages + ) + except Exception as e: + logging.error(f"Error loading products: {e}") + return f"Error loading products: {str(e)}", 500 + + +# 상품 등록 +@app.route("/register", methods=["GET", "POST"]) +def register(): + if request.method == "POST": + # 상품 등록 데이터 수집 + name = request.form.get("name") # 상품 이름 + price = float(request.form.get("price").replace('₩', '').replace(',', '')) # 판매 가격 + location = request.form.get("location") # 직거래 지역 + condition = request.form.get("condition") # 상태 + stock = int(request.form.get("stock")) # 재고 수량 + description_short = request.form.get("description_short") # 한 줄 소개 + description_long = request.form.get("description_long") # 상세 설명 + category = request.form.get("category") # 카테고리 선택 + ewha_green = request.form.get("ewha_green") == "on" # 초록템 여부 (체크박스) + + # 이미지 처리 (대표 사진 1장만) + image = request.files['file'] + image_filename = f"{name}_{image.filename}" + image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename) + image.save(image_path) + + # Firebase에 저장할 데이터 구성 + product_data = { + "name": name, + "price": price, + "location": location, + "condition": condition, + "stock": stock, + "description_short": description_short, + "description_long": description_long, + "category": category, + "ewha_green": ewha_green, + "img_path": image_filename + } + + # Firebase에 데이터 저장 + product_id = str(len(DB.get_items()) + 1) + if DB.insert_item(product_id, product_data): + return redirect(url_for("browse")) + return render_template("error.html", message="상품 등록에 실패했습니다.") + + return render_template("register.html") + + +@app.route("/login", methods=['GET', 'POST']) +def login_user(): + if request.method == 'POST': + # 로그인 처리 + id = request.form.get('id') + password = request.form.get('password') + password_hash = hashlib.sha256(password.encode('utf-8')).hexdigest() + + user = DB.find_user(id, password_hash) + + if user: + session['id'] = id + session['nickname'] = user['nickname'] # 이 부분도 DB에서 가져온 사용자 닉네임으로 수정 필요 + session['role'] = user['role'] + print(f"Session data: {session}") # 세션 확인용 출력 + return redirect(url_for("home")) + else: + flash("잘못된 ID or PW") + return render_template("login.html", error="아이디 또는 비밀번호가 잘못되었습니다.", logged_in=False) + + # GET 요청 시 로그인 화면을 렌더링 + return render_template("login.html", logged_in=False) + + +@app.route("/findId", methods=['GET', 'POST']) +def find_id(): + if request.method == "POST": + email = request.form.get("email") + + # 이메일로 아이디 찾기 + if email in users: + id = users[email]["id"] + return render_template("findId.html", id=id, found=True, logged_in=False) + else: + return render_template("findId.html", error="가입되지 않은 회원입니다.", logged_in=False) + + return render_template("findId.html", logged_in=False) + +@app.route("/logout") +def logout(): + session.clear() + return redirect(url_for("home")) + +@app.route('/review') +def reviews(): + reviews_data = [ + {"title": "리뷰 제목", "author": "작성자 닉네임", "date": "작성 날짜"}, + {"title": "리뷰 제목", "author": "작성자 닉네임", "date": "작성 날짜"}, + {"title": "리뷰 제목", "author": "작성자 닉네임", "date": "작성 날짜"}, + ] + + if session['role'] == 'seller': + return render_template("productreviewsSeller.html", reviews=reviews_data, logged_in=('id' in session), user=session.get('nickname')) + + return render_template('productreviewsBuyer.html', reviews=reviews_data) + +@app.route('/reviews', methods=['GET']) +def review_list(): + try: + # Fetch and validate reviews + reviews = DB.get_reviews() + if isinstance(reviews, dict): + reviews = list(reviews.values()) + valid_reviews = [review for review in reviews if review is not None] + + # Pagination logic + page = request.args.get('page', default=1, type=int) + reviews_per_page = 8 + total_reviews = len(valid_reviews) + total_pages = (total_reviews + reviews_per_page - 1) // reviews_per_page + + if page < 1: + page = 1 + elif page > total_pages: + page = total_pages + + start_idx = (page - 1) * reviews_per_page + end_idx = start_idx + reviews_per_page + paginated_reviews = valid_reviews[start_idx:end_idx] + + # Default handling for missing fields + for review in paginated_reviews: + review["img_path"] = review.get("img_path", "default.jpg") + + return render_template( + "reviewList.html", + reviews=paginated_reviews, + page=page, + total_pages=total_pages, + logged_in=('id' in session), + user=session.get('nickname') + ) + except Exception as e: + logging.error(f"Error loading reviews: {e}") + return f"Error loading reviews: {str(e)}", 500 + +@app.route('/myreviews') +def myreview_list(): + try: + reviews = DB.get_review_by_nickname(session.get('nickname')) + if isinstance(reviews, dict): + reviews = list(reviews.values()) + valid_reviews = [review for review in reviews if review is not None] + + # Pagination settings + page = request.args.get('page', default=1, type=int) + reviews_per_page = 8 + total_reviews = len(valid_reviews) + total_pages = (total_reviews + reviews_per_page - 1) // reviews_per_page + + if page < 1: + page = 1 + elif page > total_pages: + page = total_pages + + start_idx = (page - 1) * reviews_per_page + end_idx = start_idx + reviews_per_page + paginated_reviews = valid_reviews[start_idx:end_idx] + + # Default handling for missing fields + for review in paginated_reviews: + review["img_path"] = review.get("img_path", "default.jpg") + + return render_template( + "myreviewList.html", + reviews=paginated_reviews, + page=page, + total_pages=total_pages, + logged_in=('id' in session), + user=session.get('nickname') + ) + except Exception as e: + logging.error(f"Error loading reviews: {e}") + return f"Error loading reviews: {str(e)}", 500 + +@app.route("/reviews/register//") +def register_review_init(name): + user_id = session.get('id') + user_nickname = session.get('nickname') + return render_template("reviewRegister.html", + product_name=name, + user_id=user_id, + user_nickname=user_nickname, + user=user_nickname, + logged_in=('id' in session)) + +@app.route('/reviews/register', methods = ['GET', 'POST']) +def register_review(): + + if request.method == "POST": + review_title = request.form.get("review_title") + review_content = request.form.get("review_content") + rating = request.form.get("rating", type=int) + purchase_date = request.form.get("purchase_date") + product_name = request.form.get("product_name") + + # 이미지 파일 처리 + image = request.files['image'] + image_filename = f"{product_name}_{image.filename}" + image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename) + image.save(image_path) + + + # Firebase에 데이터 구성 + review_id = str(len(DB.get_reviews())+1) + new_review = { + "user_nickname": session.get('nickname'), + "product_name":product_name, + "review_title": review_title, + "review_content": review_content, + "rating": rating, + "img_path": image_filename, + "purchase_date": purchase_date, + "review_date": datetime.today().strftime('%Y-%m-%d'), + "review_id": review_id + } + + # Firebase에 데이터 추가 + DB.insert_review(review_id, new_review) + return redirect(url_for("review_detail", review_id=review_id)) + return render_template("register_review.html") + +@app.route('/reviews/') +def review_detail(review_id): + review = DB.get_review_by_id(review_id) + if review: + return render_template("reviewDetail.html", review=review, + logged_in=('id' in session), + user=session.get('nickname')) + return "리뷰를 찾을 수 없습니다.", 404 + +@app.route('/show_heart//', methods=['GET']) +def show_heart(name): + my_heart = DB.get_heart_byname(session['id'], name) + return jsonify({'my_heart': my_heart}) + + +@app.route('/like//', methods=['POST']) +def like(name): + my_heart = DB.update_heart(session['id'], 'Y', name) + return jsonify({'msg': '좋아요 완료!'}) + +@app.route('/unlike//', methods=['POST']) +def unlike(name): + + my_heart = DB.update_heart(session['id'], 'N', name) + return jsonify({'msg': '좋아요 취소!'}) + + + + +if __name__ == "__main__": + app.run(debug=True) \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..bfee27c --- /dev/null +++ b/database.py @@ -0,0 +1,156 @@ +import os +import pyrebase +import hashlib +import json + +class DBhandler: + def __init__(self): + with open('./Authentication/firebase_auth.json') as f: + config = json.load(f) + firebase = pyrebase.initialize_app(config) + self.db = firebase.database() + + def insert_item(self, product_id, product_data): + try: + self.db.child("items").child(product_id).set(product_data) + print(f"Item {product_id} successfully inserted into Firebase.") + return True + except Exception as e: + print(f"Error inserting item {product_id} into Firebase: {e}") + return False + + def get_items(self): + try: + items = self.db.child("items").get() + if items.val(): + print("Items successfully retrieved from Firebase.") + return items.val() + return {} + except Exception as e: + print(f"Error retrieving items from Firebase: {e}") + return {} + + def get_item_by_id(self, product_id): + try: + item = self.db.child("items").child(product_id).get() + if item.val(): + print(f"Item {product_id} successfully retrieved from Firebase.") + return item.val() + return None + except Exception as e: + print(f"Error retrieving item {product_id} from Firebase: {e}") + return None + + def user_duplicate_check(self, email): + users = self.db.child("users").get() + print("users###", users.val()) + if str(users.val()) == "None": # first registration + return True + else: + for res in users.each(): + value = res.val() + if value['email'] == email: + return False + return True + + def insert_user(self, id, password, nickname, email, phone, role): + user_data = { + "id": id, + "password": password, + "nickname": nickname, + "email": email, + "phone": phone, + "role": role + } + if self.user_duplicate_check(email): + self.db.child("users").push(user_data) + print(user_data) + return True + else: + return False + + def find_user(self, id, password): + users = self.db.child("users").get() + target_value = [] + + if not users.val(): + print("No users found") + return None + + for res in users.each(): + value = res.val() + print(f"Checking user: {value}") # data 확인 + + if 'id' in value and 'password' in value: + if value['id'] == id and value['password'] == password: + return value # 사용자 정보 반환 + return None + + + def insert_review(self, review_id, review_data): + try: + self.db.child("reviews").child(review_id).set(review_data) + print(f"Review {review_id} successfully inserted into Firebase.") + return True + except Exception as e: + print(f"Error inserting item {review_id} into Firebase: {e}") + return False + + def get_reviews(self): + try: + result = self.db.child("reviews").get() + if result.val(): + print("reviews successfully retrieved from Firebase") + return result.val() + return {} + except Exception as e: + print(f"Error retrieving reviews from Firebase: {e}") + return {} + + def get_review_by_id(self, review_id): + try: + reviews = self.db.child("reviews").get() + target_value="" + for review in reviews.each(): + key_value = review.key() + if key_value == review_id: + target_value=review.val() + return target_value + except Exception as e: + print(f"Error retrieving review {review_id} from Firebase: {e}") + return None + + def get_review_by_nickname(self, nickname): + try: + reviews = self.db.child("reviews").order_by_child("user_nickname").equal_to(nickname).get() + if reviews.val(): + print("reviews successfully retrieved from Firebase") + return reviews.val() + return {} + except Exception as e: + print(f"Error retrieving review {nickname} from Firebase: {e}") + return None + + + def get_heart_byname(self, uid, name): + hearts = self.db.child("heart").child(uid).get() + target_value = "" + + if hearts.val() is None: + return target_value + + for res in hearts.each(): + key_value = res.key() + if key_value == name: + target_value = res.val() + return target_value + + return target_value + + def update_heart(self, user_id, isHeart, item): + heart_info = { + "interested": isHeart + } + + self.db.child("heart").child(user_id).child(item).set(heart_info) + return True diff --git a/static/browseStyle.css b/static/browseStyle.css new file mode 100644 index 0000000..e9dab58 --- /dev/null +++ b/static/browseStyle.css @@ -0,0 +1,176 @@ +/* 전체 상품 리스트 스타일 */ +.product-list { + max-width: 1200px; /* 부모 컨테이너의 최대 너비 */ + margin: 0 auto; /* 중앙 정렬 */ + padding: 20px; +} + +/* 상품 리스트 제목과 토글 스위치 스타일 */ +.product-list-header { + display: flex; + align-items: center; + margin-bottom: 30px; /* 아래 간격을 늘려 상품 리스트와 여백 확보 */ + margin-top: 50px; /* 상단 간격을 늘려 네비게이션 바와 거리를 벌림 */ + gap: 20px; /* 제목과 토글 스위치 사이의 간격 */ + padding-left: 30px; /* 제목과 화면 왼쪽 사이의 간격을 벌림 */ +} + +.product-list-header h1 { + margin: 0; + font-size: 2em; /* 제목의 크기 증가 */ + font-weight: bold; /* 굵은 글씨 */ +} + +/* 상품 그리드 */ +.product-grid { + display: grid; /* 그리드 레이아웃 사용 */ + grid-template-columns: repeat(2, 1fr); /* 두 개의 열 생성 */ + gap: 30px; /* 카드 간의 간격 */ + width: 100%; +} + +/* 상품 카드 스타일 */ +.product-card { + display: flex; /* 가로 정렬 */ + flex-direction: column; /* 세로로 요소 정렬 */ + gap: 15px; /* 이미지와 텍스트 사이 간격 줄임 */ + border: 1px solid #ddd; /* 테두리 추가 */ + border-radius: 8px; /* 모서리를 둥글게 */ + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 그림자 효과 */ + background-color: #fff; /* 배경 흰색 */ + padding: 15px; /* 내부 여백 줄임 */ + align-items: center; /* 중앙 정렬 */ +} + +/* 상품 이미지 스타일 */ +.product-card img { + width: 150px; /* 이미지 너비 고정 */ + height: 150px; /* 이미지 높이 고정 */ + object-fit: cover; /* 이미지 비율 유지 */ + border-radius: 4px; +} + +/* 상품 정보 텍스트 스타일 */ +.product-info { + display: flex; + flex-direction: column; /* 텍스트 요소들을 세로로 나열 */ + justify-content: flex-start; + align-items: center; /* 텍스트를 중앙 정렬 */ + gap: 8px; /* 각 텍스트 요소 간 간격을 작게 설정 */ +} + +.product-info h3 { + font-size: 1.1em; /* 폰트 크기 약간 축소 */ + font-weight: bold; + color: #333; + margin: 0; /* 불필요한 여백 제거 */ +} + +.product-info .price { + font-weight: bold; + color: #0d6926; /* 강조된 가격 색상 */ + font-size: 1em; /* 가격 텍스트의 크기 약간 축소 */ + margin: 0; /* 여백 제거 */ +} + +.product-info .description { + color: #555; + font-size: 0.9em; /* 설명 텍스트의 크기 축소 */ + margin: 0; /* 여백 제거 */ +} + +/* 판매자 태그 */ +.seller-tag { + background-color: #6fbf73; /* 초록색 배경 */ + color: white; + padding: 3px 6px; /* 내부 여백 축소 */ + border-radius: 12px; + font-size: 0.85em; + display: inline-block; +} + +/* 페이지네이션 스타일 */ +.pagination { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.pagination-button { + padding: 10px 15px; + margin: 0 5px; + background-color: #4CAF50; + color: white; + text-decoration: none; + border-radius: 5px; + transition: background-color 0.3s; +} + +.pagination-button:hover { + background-color: #45a049; +} + +.pagination span { + margin: 0 10px; + font-size: 1.1em; + color: #333; +} + +/* 토글 스위치 스타일 */ +.toggle-switch-container { + display: flex; + align-items: center; + gap: 10px; +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; +} + +input:checked + .slider { + background-color: #4CAF50; /* 토글이 눌렸을 때 초록색으로 변경 */ +} + +input:checked + .slider:before { + transform: translateX(26px); +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} diff --git a/static/findIdStyle.css b/static/findIdStyle.css new file mode 100644 index 0000000..b267c0f --- /dev/null +++ b/static/findIdStyle.css @@ -0,0 +1,100 @@ +body { + font-family: 'Noto Sans', sans-serif; + background-color: #f9f9f9; + color: #333; + margin: 0; + padding: 0; +} + +main { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 80px; /* 모든 화면에서 폼이 일정한 높이에서 시작하도록 설정 */ + min-height: calc(100vh - 80px); +} + +.signup-form { + padding: 40px; + max-width: 650px; + width: 100%; + background-color: #ffffff; + border-radius: 10px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); +} + +.signup-form h2 { + text-align: center; + margin-bottom: 30px; + color: #006400; + font-size: 1.8em; +} + +.input-group { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 20px; + justify-content: space-between; +} + +label { + width: 20%; + font-weight: bold; +} + +input[type="text"] { + flex: 1; + padding: 12px; + border: 1px solid #ccc; + border-radius: 5px; + box-sizing: border-box; + font-size: 1em; + background-color: #f9f9f9; +} + +.check-btn { + background-color: #006400; + color: #fff; + border: none; + padding: 10px 15px; + cursor: pointer; + border-radius: 5px; + font-size: 1em; +} + +.check-btn:hover { + background-color: #228B22; +} + +.extra-links { + text-align: center; + margin-top: 20px; +} + +.extra-links a { + color: #006400; + text-decoration: none; + margin: 0 10px; + font-weight: bold; + font-size: 1em; +} + +.extra-links a:hover { + text-decoration: underline; +} + +.error-msg, .success-msg { + font-size: 0.9em; + margin-top: -10px; + margin-bottom: 15px; + display: block; +} + +.error-msg { + color: red; +} + +.success-msg { + color: #006400; +} \ No newline at end of file diff --git a/static/homeStyle.css b/static/homeStyle.css new file mode 100644 index 0000000..6474601 --- /dev/null +++ b/static/homeStyle.css @@ -0,0 +1,56 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; +} + +.welcome h2 { + padding-top:150px; + text-align: center; + font-size: 70px; +} + +.welcome p { + font-size: 20px; + text-align: center; + padding-top: 65px; +} + +.recent-sales { + margin-top: 50px; + float: left; + margin-left:50px; +} + +.recent-sales h3 { + margin-top:150px; + font-size: 24px; + color:#00462a; + text-align:left; + margin-bottom: 20px; +} + +.product-list { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 20px; +} + +.product { + border: 1px solid #ddd; + padding: 10px; + width: 300px; + text-align: center; +} + +.product img { + width: 100%; + height: 200px; + object-fit: cover; + margin-bottom: 10px; +} \ No newline at end of file diff --git a/static/images/badge.png b/static/images/badge.png new file mode 100644 index 0000000..c556c08 Binary files /dev/null and b/static/images/badge.png differ diff --git a/static/images/bunny.png b/static/images/bunny.png new file mode 100644 index 0000000..15e35e3 Binary files /dev/null and b/static/images/bunny.png differ diff --git a/static/images/keyring.png b/static/images/keyring.png new file mode 100644 index 0000000..4705679 Binary files /dev/null and b/static/images/keyring.png differ diff --git a/static/images/keyring2.jpeg b/static/images/keyring2.jpeg new file mode 100644 index 0000000..271fb0f Binary files /dev/null and b/static/images/keyring2.jpeg differ diff --git a/static/images/note.png b/static/images/note.png new file mode 100644 index 0000000..47522c1 Binary files /dev/null and b/static/images/note.png differ diff --git a/static/images/pikmin1.png b/static/images/pikmin1.png new file mode 100644 index 0000000..0bfc63c Binary files /dev/null and b/static/images/pikmin1.png differ diff --git a/static/images/pikmin2.png b/static/images/pikmin2.png new file mode 100644 index 0000000..9f76882 Binary files /dev/null and b/static/images/pikmin2.png differ diff --git a/static/images/pikmin3.png b/static/images/pikmin3.png new file mode 100644 index 0000000..f8af707 Binary files /dev/null and b/static/images/pikmin3.png differ diff --git a/static/images/product.png b/static/images/product.png new file mode 100644 index 0000000..e9fbffe Binary files /dev/null and b/static/images/product.png differ diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/static/productimage.png" b/static/images/product_detail_image.png similarity index 100% rename from "\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/static/productimage.png" rename to static/images/product_detail_image.png diff --git a/static/images/profile.webp b/static/images/profile.webp new file mode 100644 index 0000000..6ab920f Binary files /dev/null and b/static/images/profile.webp differ diff --git a/static/images/soldout.png b/static/images/soldout.png new file mode 100644 index 0000000..e112fd4 Binary files /dev/null and b/static/images/soldout.png differ diff --git a/static/images/tok.png b/static/images/tok.png new file mode 100644 index 0000000..52862ce Binary files /dev/null and b/static/images/tok.png differ diff --git a/static/indexStyle.css b/static/indexStyle.css new file mode 100644 index 0000000..4a43e86 --- /dev/null +++ b/static/indexStyle.css @@ -0,0 +1,87 @@ +body { + font-family: Arial, sans-serif; +} + +.navbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; + background-color: #f8f8f8; + border-bottom: 1px solid #ddd; +} + +.navbar__logo a { + text-decoration: none; + color: #556b2f; + font-size: 24px; + font-weight: bold; +} + +.navbar__menu { + list-style: none; + display: flex; + gap: 30px; + margin: 0; + padding: 0; +} + +.navbar__menu li a { + text-decoration: none; + color: black; + font-size: 18px; +} + +.navbar__user { + display: flex; + align-items: center; + gap: 10px; +} + +.user__role { + background-color: #556b2f; + color: white; + padding: 5px 10px; + border-radius: 12px; + font-size: 14px; +} + +.user__nickname { + font-size: 16px; + color: #333; +} + +.user__avatar { + width: 30px; + height: 30px; + background-color: #ddd; + border-radius: 50%; +} + +.login-btn { + background-color: #556b2f; + color: white; + padding: 8px 15px; + text-decoration: none; + border-radius: 5px; + font-size: 16px; + transition: background-color 0.3s; +} + +.login-btn:hover { + background-color: #228B22; +} + +.logout-btn { + background-color: #556b2f; + color: white; + padding: 8px 15px; + text-decoration: none; + border-radius: 5px; + font-size: 16px; + transition: background-color 0.3s; +} + +.logout-btn:hover { + background-color: #228B22; +} \ No newline at end of file diff --git a/static/loginStyle.css b/static/loginStyle.css new file mode 100644 index 0000000..4edc0e7 --- /dev/null +++ b/static/loginStyle.css @@ -0,0 +1,75 @@ +body { + font-family: 'Noto Sans', sans-serif; + background-color: #f9f9f9; + color: #333; + margin: 0; + padding: 0; +} + +.form-container { + padding: 40px; + max-width: 650px; + margin: 60px auto; + background-color: #ffffff; + border-radius: 10px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); +} + +.form-container h2 { + text-align: center; + margin-bottom: 30px; + color: #006400; + font-size: 1.8em; +} + +.input-group { + margin-bottom: 20px; +} + +label { + font-weight: bold; + display: block; + margin-bottom: 5px; +} + +input[type="text"], input[type="password"] { + width: calc(100% - 20px); + padding: 12px; + border: 1px solid #ccc; + border-radius: 5px; + box-sizing: border-box; + font-size: 1em; + background-color: #f9f9f9; +} + +.action-btn { + width: 100%; + padding: 15px; + background-color: #006400; + color: #fff; + border: none; + cursor: pointer; + border-radius: 5px; + font-size: 1.2em; + margin-top: 20px; +} + +.action-btn:hover { + background-color: #228B22; +} + +.extra-links { + text-align: center; + margin-top: 15px; +} + +.extra-links a { + color: #006400; + text-decoration: none; + margin: 0 10px; + font-weight: bold; +} + +.extra-links a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/static/main.js b/static/main.js new file mode 100644 index 0000000..f3b4e99 --- /dev/null +++ b/static/main.js @@ -0,0 +1,60 @@ +$(document).ready(function() { + + showHeart(); +}); + + +function showHeart() { + $.ajax({ + type: 'GET', + url: '/show_heart/{{name}}/', + data: {}, + success: function(response) { + let my_heart = response['my_heart']; + if (my_heart && my_heart['interested'] === 'Y') { + + $("#heart").css("color", "red"); + $("#heart").attr("onclick", "unlike()"); + } else { + + $("#heart").css("color", "grey"); + $("#heart").attr("onclick", "like()"); + } + }, + error: function(request, status, error) { + console.error("AJAX error:", error); + } + }); +} + + +function like() { + $.ajax({ + type: 'POST', + url: '/like/{{name}}/', + data: { interested: 'Y' }, + success: function(response) { + alert(response['msg']); + window.location.reload(); + }, + error: function(request, status, error) { + console.error("AJAX error:", error); + } + }); +} + + +function unlike() { + $.ajax({ + type: 'POST', + url: '/unlike/{{name}}/', + data: { interested: 'N' }, + success: function(response) { + alert(response['msg']); + window.location.reload(); + }, + error: function(request, status, error) { + console.error("AJAX error:", error); + } + }); +} \ No newline at end of file diff --git a/static/mypageStyle.css b/static/mypageStyle.css new file mode 100644 index 0000000..089752c --- /dev/null +++ b/static/mypageStyle.css @@ -0,0 +1,135 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + + +body { + font-family: Arial, sans-serif; + background-color: white; + line-height: 1.8; +} + +.container { + display: flex; + margin: 0 auto; +} + +.sidebar { + width: 25%; + background-color: #eeeeee; + color: #36AE92; + padding: 20px; + min-height: 100vh; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; +} + +.sidebar .profile-pic { + width: 100px; + height: 100px; + border-radius: 100%; + margin-bottom: 10px; + overflow: hidden; +} + +.profile-pic img { + width: 100%; + height: 100%; + object-fit: cover; + align-content: center; +} + +.sidebar p { + font-size: 18px; +} + +.profile { + margin-top: 20px; + padding: 10px; + background-color: #36AE92; + border: none; + color: white; + cursor: pointer; + width: 100%; + border-radius: 5px; +} +.sidebtn { + margin-top: 20px; + padding: 10px; + background-color: #00462a; + border: none; + color: white; + cursor: pointer; + width: 100%; + border-radius: 5px; +} + +.main-board { + width: 75%; + padding: 15px; +} + +.mainbtn { + padding:5px; + background-color: #00462a; + border:none; + color: white; + cursor: pointer; + border-radius: 5px; + font-size:15px; +} +.myinfo { + display: flex; + justify-content: space-between; + margin-bottom: 30px; +} + +.myinfo h1 { + font-size: 50px; + font-weight: bold; +} + +.myinfo h2 { + font-size: 35px; + font-weight: bold; +} + +.myinfo h3 { + font-size: 21px; + font-weight: bold; +} + +.myinfo .info-item { + background-color: white; + padding: 20px; + border-radius: 10px; + width: 48%; + text-align: center; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.sales-list table { + margin-top: 20px; + width: 100%; + padding: 20px; + border-radius: 10px; + width: 95%; + background-color: white; + align-content: center; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.sales-list table th, .sales-list table td { + padding: 15px; + border: 1px solid #ccc; + border-radius: 5px; +} + +.sales-list table th { + background-color: #00462a; + color:white; +} \ No newline at end of file diff --git a/static/productDetailStyle.css b/static/productDetailStyle.css new file mode 100644 index 0000000..ea01d12 --- /dev/null +++ b/static/productDetailStyle.css @@ -0,0 +1,59 @@ +/* 전체 상품 상세 스타일 */ +.product-detail { + max-width: 800px; /* 페이지 너비 제한 */ + margin: 0 auto; /* 중앙 정렬 */ + padding: 20px; +} + +/* 상품 정보와 이미지를 나란히 배치하는 컨테이너 */ +.product-container { + display: flex; /* Flexbox를 사용하여 이미지와 정보를 나란히 배치 */ + align-items: flex-start; + gap: 20px; /* 이미지와 정보 사이의 간격 */ +} + +/* 상품 이미지 크기 조절 */ +.product-container img { + width: 300px; /* 고정된 이미지 너비 */ + max-height: 400px; /* 이미지 최대 높이 제한 */ + object-fit: cover; /* 비율 유지하며 자르기 */ + border-radius: 8px; /* 모서리를 약간 둥글게 */ + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 이미지에 약간의 그림자 */ +} + +/* 상품 정보 스타일 */ +.product-info { + flex: 1; /* 남은 공간을 차지하게 하기 */ + display: flex; + flex-direction: column; + justify-content: space-between; +} + +/* 상품 정보 텍스트 스타일 */ +.product-info p { + margin-bottom: 10px; /* 각 정보 간의 간격 */ + font-size: 1em; + color: #555; +} + +/* 행동 버튼 스타일 */ +.actions { + margin-top: 20px; /* 상품 정보 아래 여유 공간 */ + display: flex; + gap: 10px; /* 버튼 간의 간격 */ +} + +.actions button { + padding: 10px 20px; + font-size: 1em; + background-color: #4CAF50; /* 버튼 배경 색상 */ + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s; +} + +.actions button:hover { + background-color: #45a049; /* 호버 시 배경 색상 변경 */ +} diff --git a/static/product_detail_style.css b/static/product_detail_style.css new file mode 100644 index 0000000..e00f974 --- /dev/null +++ b/static/product_detail_style.css @@ -0,0 +1,159 @@ +/* Main content */ +main { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 80px; +} + +.product-details { + display: flex; + width: 100%; + max-width: 1200px; + justify-content: space-between; + background-color: white; + padding: 30px; + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + align-items: stretch; + margin-top: 20px; + margin-left: auto; + margin-right: auto; +} + +/* Left and Right Sections */ +.left-section, +.right-section { + width: 48%; + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +/* Product image */ +.image-container { + position: relative; + margin-bottom: 20px; +} + +.image-container h2 { + position: absolute; + top: -30px; + left: 10px; + font-size: 20px; + font-weight: bold; +} + +.product-image { + width: 100%; + max-width: 350px; + height: auto; +} + +/* Product info */ +.product-info p, +.right-section p { + margin: 10px 0; + line-height: 1.6; +} + +.product-info p strong, +.right-section p strong { + font-weight: bold; +} + +.stars { + color: #FFD700; + font-size: 20px; +} + +/* Reviews */ +.reviews { + margin-top: 30px; +} + +.review-list { + margin-top: 15px; +} + +.review { + margin-bottom: 15px; + padding-bottom: 15px; + border-bottom: 1px solid #ddd; +} + +.review-actions { + display: flex; + justify-content: flex-end; + margin-top: 10px; +} + +.more-reviews { + text-decoration: none; + color: #4CAF50; + font-weight: bold; +} + +/* Purchase Section */ +.purchase-section { + margin-top: 30px; + display: flex; + justify-content: flex-start; + gap: 20px; +} + +.purchase-section button { + padding: 15px 30px; + background-color: white; + color: #4CAF50; + border: 2px solid #4CAF50; + border-radius: 0; + cursor: pointer; + font-size: 16px; + flex-grow: 1; +} + + +.purchase-section .purchase-button { + width: 200px; +} + +.purchase-section button:hover { + background-color: #4CAF50; + color: white; +} + +/* Review Button */ +.review-button { + background-color: white; + color: #008CBA; + border: 2px solid #008CBA; +} + +.review-button:hover { + background-color: #008CBA; + color: white; +} + +/* Like Button */ +.like-button { + display: flex; + align-items: center; + padding: 10px 20px; + background-color: white; + color: #FF4081; + border: 2px solid #FF4081; + border-radius: 0; + cursor: pointer; + font-size: 16px; +} + +.like-button:hover { + background-color: #FF4081; + color: white; +} + +.heart-icon { + margin-right: 10px; + font-size: 20px; +} diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/static/productreviewsStyle.css" b/static/productreviewsStyle.css similarity index 100% rename from "\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/static/productreviewsStyle.css" rename to static/productreviewsStyle.css diff --git a/static/registerStyle.css b/static/registerStyle.css new file mode 100644 index 0000000..3c962c4 --- /dev/null +++ b/static/registerStyle.css @@ -0,0 +1,109 @@ +.form-page { + padding-top: 40px; /* Ensures space for the fixed navbar */ +} + +.form-page h1 { + position: relative; + top: -20px; /* 제목만 위로 이동 */ + margin-left: 20px; /* 제목을 오른쪽으로 이동 */ + margin-bottom: 20px; /* 아래 항목과의 간격 조정 */ +} + +.form-container { + display: flex; + justify-content: space-evenly; + width: 100%; + margin-bottom: 20px; + flex-wrap: wrap; + padding-left: 85px; /* 왼쪽 테두리와의 간격 추가 */ + margin-top: -20px; /* 폼 전체를 위로 이동 */ +} + + +.left-column, .right-column { + width: 48%; +} + +.left-column label, +.right-column label { + font-weight: bold; + display: block; + margin-bottom: 2px; /* 라벨 아래의 간격 조정 */ + color: #666; +} + +.left-column input, +.left-column select, +.right-column input, +.right-column select, +.right-column textarea { + width: 70%; + padding: 8px; + margin-top: 5px; /* 라벨과 입력 필드 사이의 간격 */ + margin-bottom: 10px; + border: 1px solid #ddd; + border-radius: 5px; + box-sizing: border-box; +} + +.radio-group { + display: flex; + align-items: center; /* 수직 정렬 */ + gap: 15px; /* 버튼 간의 간격 조정 */ +} + +.radio-button { + display: inline-block; + padding: 5px 15px; + background-color: #f0f0f0; /* 기본 배경색 */ + border: 1px solid #ccc; /* 테두리 색 */ + border-radius: 20px; /* 모서리 둥글게 */ + cursor: pointer; /* 커서 모양 변경 */ + text-align: center; /* 텍스트 중앙 정렬 */ + transition: background-color 0.3s, color 0.3s; /* 부드러운 전환 효과 */ +} + +/* 라디오 버튼 내부의 텍스트 두께 설정 */ +.radio-button span { + font-weight: normal; +} + +.radio-button input[type="radio"] { + display: none; /* 기본 라디오 버튼 숨기기 */ +} + +.radio-button:hover { + background-color: #e0e0e0; /* 호버 시 배경색 변경 */ +} + +/* 선택된 버튼 스타일 */ +.radio-button input[type="radio"]:checked + .radio-button { + background-color: #00462A; /* 선택된 버튼 배경색 */ + color: white; /* 선택된 버튼 텍스트 색 */ + border: 1px solid #0056b3; /* 선택된 버튼 테두리 색 */ +} + +/* 눌린 상태 스타일 */ +.radio-button input[type="radio"]:checked + label { + background-color: #00462A; /* 선택된 버튼 배경색 */ + color: white; /* 선택된 버튼 텍스트 색 */ +} + + +/* 수정된 부분: submit 버튼 스타일 */ +form input[type="submit"] { + background-color: #007bff; + color: white; + border: none; + padding: 10px; + border-radius: 6px; + cursor: pointer; + min-width: 50%; /* 너비 설정 */ + display: block; /* 블록 요소로 설정 */ + margin: 0 0 0 0 10%; /* 왼쪽으로 약간 이동 */ +} + + +form input[type="submit"]:hover { + background-color: #0056b3; +} diff --git a/static/reviewDetailStyle.css b/static/reviewDetailStyle.css new file mode 100644 index 0000000..de79d85 --- /dev/null +++ b/static/reviewDetailStyle.css @@ -0,0 +1,156 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f9f9f9; +} + +.back-link { + padding: 5px 10px; + color: white; + background-color: #333; + font-size: 16px; + text-decoration: none; + margin-bottom: 24px; + display: inline-block; + border: 1px solid; + border-radius: 4px; + transition: background-color 0.3s; +} + +.back-link:hover { + background-color: #555; +} + +.review-detail { + max-width: 1000px; + margin: 40px auto; + padding: 20px; +} + +.review-container { + display: flex; + gap: 40px; + background-color: #fff; + padding: 30px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.product-section, +.review-section { + flex: 1; +} + +.product-section { + text-align: left; +} + +.product-info { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; + justify-content: flex-start; +} + +.product-info h2 { + font-size: 20px; + font-weight: bold; + color: #333; + margin-bottom: 4px; +} + +.product-image { + width: 100%; + height: 200px; + background-color: #f0f0f0; + border: 1px solid #ddd; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + margin-bottom: 10px; +} + +.product-image img { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.rating { + font-size: 18px; + color: #333; +} + +.purchase-date { + padding: 5px 0; + font-size: 14px; + color: black; + text-align: left; +} + +.review-section { + padding-left: 20px; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.review-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} + +.nickname { + font-size: 16px; + font-weight: bold; + color: #333; +} + +.review-date { + font-size: 14px; + color: black; + margin-top: 10px; +} + +.review-box { + border: 1px solid #ddd; + border-radius: 8px; + padding: 16px; + background-color: #f9f9f9; + margin-bottom: 10px; + flex-grow: 1; /* Ensures the review content fills remaining space */ +} + +.review-box h3 { + font-size: 16px; + font-weight: bold; + margin-bottom: 8px; +} + +.review-box p { + font-size: 14px; + color: #333; + line-height: 1.5; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .review-container { + flex-direction: column; + gap: 20px; + } + + .product-section, + .review-section { + padding: 0; + } +} diff --git a/static/reviewListStyle.css b/static/reviewListStyle.css new file mode 100644 index 0000000..3a4332f --- /dev/null +++ b/static/reviewListStyle.css @@ -0,0 +1,105 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +main { + padding: 32px; + max-width: 1200px; + margin: auto; +} + +h1 { + font-size: 24px; + margin-bottom: 16px; + text-align: left; +} + +/* Review Grid */ +.review-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; +} + +.review-card { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 5px; + padding: 16px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.image-placeholder { + width: 150px; + height: 150px; + background-color: #e0e0e0; + border-radius: 5px; + margin-bottom: 16px; +} + +.image-placeholder img { + width: 100%; + height: 100%; + object-fit: contain; + border-radius: 5px; +} + +.review-details { + width: 100%; + border-top: 1px solid #ddd; + padding-top: 16px; +} + +.title { + font-weight: bold; + font-size: 18px; + margin-bottom: 8px; + text-align: left; +} + +.nickname-rating{ + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} + +.nickname, .rating{ + font-size: 16px; +} + + +/* Pagination Styling */ +.pagination { + display: flex; + justify-content: center; + align-items: center; + margin-top: 16px; +} + +.page-arrow, +.page-number { + margin: 10px; + text-decoration: none; + color: #333; + font-size: 16px; +} + +.page-number.active { + font-weight: bold; + color: #4a5c43; +} + +.page-arrow { + font-size: 20px; +} + +.disabled { + pointer-events: none; + opacity: 0.5; +} diff --git a/static/reviewRegisterStyle.css b/static/reviewRegisterStyle.css new file mode 100644 index 0000000..fe48d85 --- /dev/null +++ b/static/reviewRegisterStyle.css @@ -0,0 +1,144 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #ffffff; +} + +.review-form { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.form-grid { + display: grid; + grid-template-columns: auto 1fr; + gap: 40px; + align-items: start; + margin-top: 30px; +} + +.image-section { + width: 400px; +} + +.product-image-placeholder { + width: 100%; + height: 400px; + background-color: #f0f0f0; + border: 1px solid #ddd; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; + position: relative; +} + +.product-image-placeholder img { + width: 100%; + height: 100%; + object-fit: contain; +} + +#product-image { + max-width: 100%; + max-height: 100%; + object-fit: contain; + transition: opacity 0.3s ease; +} + +.file-input-wrapper { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); +} + +.star-rating { + display: flex; + flex-direction: row-reverse; + justify-content: center; + gap: 10px; + margin-top: 20px; +} + +.star-rating input { + display: none; +} + +.star-rating label { + font-size: 30px; + color: #ddd; + cursor: pointer; +} + +.star-rating input:checked ~ label { + color: #ffd700; +} + +.star-rating label:hover, +.star-rating label:hover ~ label { + color: #ffd700; +} + +.form-fields { + display: flex; + flex-direction: column; + gap: 20px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.form-group label { + font-size: 14px; + color: #333; +} + +.form-control { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; +} + +select.form-control { + background-color: #fff; +} + +textarea.form-control { + min-height: 120px; + resize: vertical; +} + +.character-count { + text-align: right; + font-size: 12px; + color: #666; +} + +.submit-button { + padding: 8px 24px; + background-color: #333; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + align-self: flex-end; +} + +.submit-button:hover { + background-color: #444; +} + +input[readonly] { + background-color: #f5f5f5; +} \ No newline at end of file diff --git a/static/signUpStyle.css b/static/signUpStyle.css new file mode 100644 index 0000000..ed4a1d8 --- /dev/null +++ b/static/signUpStyle.css @@ -0,0 +1,152 @@ +body { + font-family: 'Noto Sans', sans-serif; + background-color: #f9f9f9; + color: #333; + margin: 0; + padding: 0; +} + +.signup-form { + padding: 40px; + max-width: 650px; /* 사진의 비율에 맞게 조정 */ + margin: 40px auto; + background-color: #ffffff; + border-radius: 10px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); +} + +.signup-form h2 { + text-align: center; + margin-bottom: 30px; + color: #006400; + font-size: 1.8em; +} + +.input-group { + display: flex; + align-items: center; + gap: 10px; /* 입력 필드와 버튼 간의 여백을 조정 */ + margin-bottom: 20px; + justify-content: space-between; +} + +label { + width: 20%; + font-weight: bold; +} + +input[type="text"], input[type="password"], input[type="file"], select { + flex: 1; + padding: 12px; + border: 1px solid #ccc; + border-radius: 5px; + box-sizing: border-box; + font-size: 1em; + background-color: #f9f9f9; +} + +.email-field { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 15px; /* 다른 입력 필드와 동일한 마진 추가 */ + width: 100%; /* 입력 필드를 우측 정렬되도록 동일한 크기로 확장 */ +} + +.email-field input[type="text"], +.email-field select, +#custom-domain { + flex: 1; /* 모든 항목의 크기를 동일하게 설정 */ + padding: 12px; + border: 1px solid #ccc; + border-radius: 8px; + box-sizing: border-box; + font-size: 1em; +} + +#custom-domain { + display: none; +} + +.radio-group { + display: flex; + gap: 20px; +} + +.role-group label { + font-weight: bold; +} + +.role-label { + padding: 10px 20px; + border-radius: 20px; + border: 1px solid #ccc; + cursor: pointer; + transition: all 0.3s ease-in-out; + background-color: #f9f9f9; +} + +input[type="radio"]:checked + .role-label { + background-color: #006400; + color: #fff; + border-color: #006400; +} + +.profile-upload { + border: 2px dashed #ccc; + padding: 10px; + border-radius: 5px; + transition: border-color 0.3s; + background-color: #f9f9f9; +} + +.profile-upload:hover { + border-color: #006400; +} + +.phone-group input[type="text"] { + width: 25%; + box-sizing: border-box; +} + +.check-btn { + background-color: #006400; + color: #fff; + border: none; + padding: 10px 15px; + cursor: pointer; + border-radius: 5px; + font-size: 1em; +} + +.check-btn:hover { + background-color: #228B22; +} + +.signup-btn { + width: 100%; + padding: 15px; + background-color: #006400; + color: #fff; + border: none; + cursor: pointer; + border-radius: 5px; + font-size: 1.2em; + margin-top: 25px; +} + +.signup-btn:hover { + background-color: #228B22; +} + +.hidden { + display: none; +} + +.error-msg { + color: red; + font-size: 0.9em; + margin-top: -10px; + margin-bottom: 15px; + display: block; +} \ No newline at end of file diff --git a/static/signUpValidation.js b/static/signUpValidation.js new file mode 100644 index 0000000..7947e6f --- /dev/null +++ b/static/signUpValidation.js @@ -0,0 +1,63 @@ +document.addEventListener("DOMContentLoaded", function() { + // Error messages initially hidden + document.querySelectorAll(".error-msg").forEach(function(element) { + element.style.display = "none"; + }); + + // Domain select change event listener + document.getElementById('domain-select').addEventListener('change', function() { + const customDomainInput = document.getElementById('custom-domain'); + if (this.value === 'custom') { + customDomainInput.style.display = 'inline-block'; + this.style.display = 'none'; + } else { + customDomainInput.style.display = 'none'; + } + }); + + // Form validation for showing error messages + document.getElementById('signup-form').addEventListener('submit', function(event) { + let hasError = false; + + // User ID validation + const userId = document.getElementById('id').value.trim(); + if (userId === "") { + document.getElementById('id-error').style.display = "block"; + hasError = true; + } else { + document.getElementById('id-error').style.display = "none"; + } + + // Password validation + const password = document.getElementById('password').value.trim(); + if (password === "") { + document.getElementById('password-error').style.display = "block"; + hasError = true; + } else { + document.getElementById('password-error').style.display = "none"; + } + + // Confirm Password validation + const confirmPassword = document.getElementById('confirm-password').value.trim(); + if (confirmPassword !== password || confirmPassword === "") { + document.getElementById('confirm-password-error').style.display = "block"; + hasError = true; + } else { + document.getElementById('confirm-password-error').style.display = "none"; + } + + // Nickname validation + const nickname = document.getElementById('nickname').value.trim(); + if (nickname === "") { + document.getElementById('nickname-error').style.display = "block"; + hasError = true; + } else { + document.getElementById('nickname-error').style.display = "none"; + } + + // Prevent form submission if there are errors + if (hasError) { + event.preventDefault(); + } + }); +}); \ No newline at end of file diff --git a/templates/browseBuyer.html b/templates/browseBuyer.html new file mode 100644 index 0000000..91791da --- /dev/null +++ b/templates/browseBuyer.html @@ -0,0 +1,62 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+

{{ "이화그린 상품 리스트" if green_view else "전체 상품 리스트" }}

+
+ + +
+
+ +
+ {% if products and products|length > 0 %} +
+ {% for product in products %} +
+ 상품 이미지 +
+

{{ product.name }}

+

₩ {{ product.price | int }}

+

{{ product.description_short }}

+
+
+ {% endfor %} +
+ + + + {% else %} +

현재 상품이 없습니다.

+ {% endif %} +
+ + +{% endblock content %} diff --git a/templates/browseBuyerEwhaGreen.html b/templates/browseBuyerEwhaGreen.html new file mode 100644 index 0000000..a60780e --- /dev/null +++ b/templates/browseBuyerEwhaGreen.html @@ -0,0 +1,52 @@ + + + + + + + 이화그린 상품 리스트 + + + +
+

이화그린 상품 리스트

+
+ + +
+
+ +
+ +

이화그린 상품 리스트가 여기에 표시됩니다.

+
+ + + + + diff --git a/templates/browseSeller.html b/templates/browseSeller.html new file mode 100644 index 0000000..fa3f40e --- /dev/null +++ b/templates/browseSeller.html @@ -0,0 +1,48 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+

{{ "이화그린 상품 리스트" if green_view else "상품 리스트" }}

+
+ + +
+
+ +
+ {% if products and products|length > 0 %} +
+ {% for product in products %} +
+ 상품 이미지 +
+

{{ product.name }}

+

₩ {{ product.price | int }}

+

{{ product.description_short }}

+
+
+ {% endfor %} +
+ {% else %} +

상품이 없습니다.

+ {% endif %} +
+ + +{% endblock content %} diff --git a/templates/browseSellerEwhaGreen.html b/templates/browseSellerEwhaGreen.html new file mode 100644 index 0000000..c4d5189 --- /dev/null +++ b/templates/browseSellerEwhaGreen.html @@ -0,0 +1,53 @@ + + + + + + + + 이화그린 상품 리스트 + + + +
+

이화그린 상품 리스트

+
+ + +
+
+ +
+ +

이화그린 상품 리스트가 여기에 표시됩니다.

+
+ + + + + diff --git a/templates/findId.html b/templates/findId.html new file mode 100644 index 0000000..c010fec --- /dev/null +++ b/templates/findId.html @@ -0,0 +1,30 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + + +{% endblock %} + +{% block content %} +
+ +
+{% endblock content %} \ No newline at end of file diff --git a/templates/homeBuyer.html b/templates/homeBuyer.html new file mode 100644 index 0000000..486a07c --- /dev/null +++ b/templates/homeBuyer.html @@ -0,0 +1,38 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+

안녕하세요, {{session['nickname']}}

+

오늘도 특별한 상품들이 기다리고 있습니다. 즐거운 쇼핑 되세요!

+
+ +
+

Recent Sales

+
+
+ badge +
badge
+
+
+ bunny doll +
bunny
+
+
+ keyring +
keyring
+
+
+ note +
note
+
+
+ Ewha +
tok
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/homeSeller.html b/templates/homeSeller.html new file mode 100644 index 0000000..8e25659 --- /dev/null +++ b/templates/homeSeller.html @@ -0,0 +1,38 @@ +{% extends "indexSeller.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+

안녕하세요, {{session['nickname']}}

+

제품을 판매할 준비가 되셨나요? 성공적인 하루를 기원합니다!

+
+ +
+

Recent Sales

+
+
+ badge +
badge
+
+
+ bunny doll +
bunny
+
+
+ keyring +
keyring
+
+
+ note +
note
+
+
+ Ewha +
tok
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/indexBuyer.html b/templates/indexBuyer.html new file mode 100644 index 0000000..bc34c9b --- /dev/null +++ b/templates/indexBuyer.html @@ -0,0 +1,41 @@ + + + + + + Ewha Market + + + + + + + {% block head %}{% endblock %} + + + + + {% block content %} + {% endblock %} + + + diff --git a/templates/indexSeller.html b/templates/indexSeller.html new file mode 100644 index 0000000..4c2e77c --- /dev/null +++ b/templates/indexSeller.html @@ -0,0 +1,42 @@ + + + + + + Ewha Market + + + + + + + + {% block head %}{% endblock %} + + + + + {% block content %} + {% endblock %} + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..efd68a2 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,38 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + + +{% endblock %} + +{% block content %} +
+
+

Ewha Market에 오신 걸 환영해요!
회원님의 계정을 생성해볼까요?

+ + {% with mesg = get_flashed_messages() %} + {% if mesg !=[] %} + + {% endif %} + {% endwith %} + +
+
+ + +
+ +
+ + +
+ + +
+ +
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/mypageBuy.html b/templates/mypageBuy.html new file mode 100644 index 0000000..0f40c1a --- /dev/null +++ b/templates/mypageBuy.html @@ -0,0 +1,98 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+ + + + +
+

이화인(아이디)님의 구매 현황

+ +
+
+

누적 주문 수

+

14

+ +
+ +
+

작성한 리뷰 수

+

9

+ +
+
+ + +
+

이화인(ID)님의 주문 리스트

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
주문번호상품이름카테고리주문수량주문일시상태
001상품 A카테고리 A22024-10-23배송중
001상품 A카테고리 A22024-10-23배송중
001상품 A카테고리 A22024-10-23배송중
001상품 A카테고리 A22024-10-23배송중
001상품 A카테고리 A22024-10-23배송중
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/mypageSell.html b/templates/mypageSell.html new file mode 100644 index 0000000..ac4833a --- /dev/null +++ b/templates/mypageSell.html @@ -0,0 +1,93 @@ +{% extends "indexSeller.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+ + + + +
+

스토어 "이화인 마켓"의 판매 현황

+ +
+
+

리뷰 수 / 주문 수


+

12 / 45

+
+ +
+

스토어 평점

+

★★★★☆
4.0

+
+
+ + +
+

스토어 "이화인 마켓"의 상품 리스트

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
상품번호상품이름카테고리재고주문수량
001상품 A카테고리 A10050
001상품 A카테고리 A10050
001상품 A카테고리 A10050
001상품 A카테고리 A10050
001상품 A카테고리 A10050
001상품 A카테고리 A10050
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/myreviewList.html b/templates/myreviewList.html new file mode 100644 index 0000000..85d581c --- /dev/null +++ b/templates/myreviewList.html @@ -0,0 +1,45 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} + +
+

My 리뷰

+
+ + {% for review in reviews %} +
+
+ Review Image +
+
+
{{ review['review_title'] }}
+
+
{{review['user_nickname']}}
+
{{review['rating']}} ★
+
+
+
+ {% endfor %} +
+ + + +
+ +{% endblock content %} diff --git a/templates/productDetailBuyer.html b/templates/productDetailBuyer.html new file mode 100644 index 0000000..bb32760 --- /dev/null +++ b/templates/productDetailBuyer.html @@ -0,0 +1,61 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+
+

상품 상세 조회

+ + + Product Image +
+
+ +

초록템 {{ 'YES' if product['ewha_green'] else 'NO' }}

+

카테고리 {{ product['category'] }}

+

상품 이름 {{ product['name'] }}

+

판매 가격 {{ product['price'] }}

+

한 줄 소개 {{ product['description_short']}}

+
+
+ +
+

거래 지역 {{ product['location'] }}

+

상품 상태 {{ product['condition'] }}

+

재고 수량 {{ product['stock'] }}

+

상세 설명 {{ product['description_long'] }}

+ + +
+

리뷰

+

평점 ★★★★★ 5.0

+
+ {% for review in reviews %} +
+

{{ review['user_id'] }} 님의 리뷰

+

평점 ★{{ review['rating'] }}

+

{{ review['content'] }}

+
+ {% endfor %} +
+ +
+ +
+ + + + + +
+
+
+{% endblock %} diff --git a/templates/productDetailSeller.html b/templates/productDetailSeller.html new file mode 100644 index 0000000..775723c --- /dev/null +++ b/templates/productDetailSeller.html @@ -0,0 +1,61 @@ +{% extends "indexSeller.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+
+

상품 상세 조회

+ + + Product Image +
+
+ +

초록템 {{ 'YES' if product['ewha_green'] else 'NO' }}

+

카테고리 {{ product['category'] }}

+

상품 이름 {{ product['name'] }}

+

판매 가격 {{ product['price'] }}

+

한 줄 소개 {{ product['description_short']}}

+
+
+ +
+

거래 지역 {{ product['location'] }}

+

상품 상태 {{ product['condition'] }}

+

재고 수량 {{ product['stock'] }}

+

상세 설명 {{ product['description_long'] }}

+ + +
+

리뷰

+

평점 ★★★★ 4.0

+
+ {% for review in reviews %} +
+

{{ review['user_id'] }} 님의 리뷰

+

평점 ★{{ review['rating'] }}

+

{{ review['content'] }}

+
+ {% endfor %} +
+ +
+ +
+ + + + + +
+
+
+{% endblock %} diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/templates/productdetail.html" b/templates/product_detail.html similarity index 92% rename from "\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/templates/productdetail.html" rename to templates/product_detail.html index 3baaae0..d5b0c4d 100644 --- "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/templates/productdetail.html" +++ b/templates/product_detail.html @@ -1,10 +1,10 @@ - + {{ product['name'] }} - 상품 상세 조회 - + @@ -28,7 +28,7 @@

Ewha Market

상품 상세 조회

상품 상세 조회

- 상품 이미지 + 상품 이미지
@@ -74,4 +74,4 @@

리뷰

- \ No newline at end of file + \ No newline at end of file diff --git a/templates/productreviewsBuyer.html b/templates/productreviewsBuyer.html new file mode 100644 index 0000000..cb359a4 --- /dev/null +++ b/templates/productreviewsBuyer.html @@ -0,0 +1,51 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+

{{ product_name }}

+

상품 리뷰 조회

+
+ {% if reviews %} + {% for review in reviews %} +
+ + {% if review['img_path'] %} + Review Image + {% else %} +
+ {% endif %} + + +
+
+ + ★{{ review['rating'] }} + +

{{ review['review_title'] }}

+

{{ review['user_nickname'] }}

+
+ 전체 보기 > +

작성 날짜: {{ review['review_date'] }}

+
+
+ {% endfor %} + {% else %} +

이 상품에 대한 리뷰가 없습니다.

+ {% endif %} +
+ + + +
+
+{% endblock %} diff --git a/templates/productreviewsSeller.html b/templates/productreviewsSeller.html new file mode 100644 index 0000000..cb359a4 --- /dev/null +++ b/templates/productreviewsSeller.html @@ -0,0 +1,51 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+

{{ product_name }}

+

상품 리뷰 조회

+
+ {% if reviews %} + {% for review in reviews %} +
+ + {% if review['img_path'] %} + Review Image + {% else %} +
+ {% endif %} + + +
+
+ + ★{{ review['rating'] }} + +

{{ review['review_title'] }}

+

{{ review['user_nickname'] }}

+
+ 전체 보기 > +

작성 날짜: {{ review['review_date'] }}

+
+
+ {% endfor %} + {% else %} +

이 상품에 대한 리뷰가 없습니다.

+ {% endif %} +
+ + + +
+
+{% endblock %} diff --git a/templates/register.html b/templates/register.html new file mode 100644 index 0000000..e8e4d69 --- /dev/null +++ b/templates/register.html @@ -0,0 +1,60 @@ +{% extends "indexSeller.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+

상품 등록

+
+
+
+
+

+ +
+

+ +
+

+ +
+

+ +
+

+
+
+
+

+ +
+

+ +
+

+ +
+

+ +
+

+ + +
+
+
+
+{% endblock content %} diff --git a/templates/reviewDetail.html b/templates/reviewDetail.html new file mode 100644 index 0000000..05ccd17 --- /dev/null +++ b/templates/reviewDetail.html @@ -0,0 +1,34 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+ 전체 리뷰 +
+
+
+

{{review['product_name']}}

+
{{review['rating']}} ★
+
+
+ Product Image +
+

구매 날짜: {{review['purchase_date']}}

+
+ +
+
+

{{review['user_nickname']}}

+
+
+

{{review['review_title']}}

+

{{review['review_content']}}

+
+

작성 날짜: {{review['review_date']}}

+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/reviewList.html b/templates/reviewList.html new file mode 100644 index 0000000..f637c57 --- /dev/null +++ b/templates/reviewList.html @@ -0,0 +1,47 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} + +
+

전체 리뷰

+
+ + {% for review in reviews %} +
+
+ Product Image +
+
+
{{ review['review_title'] }}
+
+
{{ review['user_nickname'] }}
+
{{ review['rating'] }} ★
+
+
+
+ {% endfor %} +
+ + + +
+ +{% endblock content %} diff --git a/templates/reviewRegister.html b/templates/reviewRegister.html new file mode 100644 index 0000000..477ece3 --- /dev/null +++ b/templates/reviewRegister.html @@ -0,0 +1,114 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+
+
+
+ Preview Image +
+ +
+
+ +
+ + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+ +
+ + + +
+ +
+ + +
+ +
+ + +
0/100
+
+ + + + +
+
+
+
+ +{% block scripts %} + +{% endblock %} + +{% endblock content %} \ No newline at end of file diff --git a/templates/signUp.html b/templates/signUp.html new file mode 100644 index 0000000..7002358 --- /dev/null +++ b/templates/signUp.html @@ -0,0 +1,81 @@ +{% extends "indexBuyer.html" %} + +{% block head %} + + +{% endblock %} + +{% block content %} +
+ +
+ + +{% endblock content %} \ No newline at end of file diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/app.py" "b/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/app.py" deleted file mode 100644 index ba3b439..0000000 --- "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/app.py" +++ /dev/null @@ -1,15 +0,0 @@ -from flask import Flask, render_template - -app = Flask(__name__) - -@app.route('/productreviews') -def reviews(): - reviews_data = [ - {"title": "리뷰 제목", "author": "작성자 닉네임", "date": "작성 날짜"}, - {"title": "리뷰 제목", "author": "작성자 닉네임", "date": "작성 날짜"}, - {"title": "리뷰 제목", "author": "작성자 닉네임", "date": "작성 날짜"}, - ] - return render_template('productreviews.html', reviews=reviews_data) - -if __name__ == '__main__': - app.run(debug=True) diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/templates/productreviews.html" "b/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/templates/productreviews.html" deleted file mode 100644 index 9a2c0fe..0000000 --- "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\352\260\234\353\263\204 \354\203\201\355\222\210 \353\246\254\353\267\260 \354\240\204\354\262\264 \354\241\260\355\232\214/templates/productreviews.html" +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - 상품 리뷰 조회 - - - -
-

Ewha Market

- -
- -
-
-

상품명

-

전체 리뷰 조회

-
- {% for i in range(5) %} -
-
-
-
- ★★★★★ -

리뷰 제목

-

작성자 닉네임

-
- 전체 보기 > -

작성 날짜

-
-
- {% endfor %} -
- -
-
- - \ No newline at end of file diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/__pycache__/app.cpython-310.pyc" "b/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/__pycache__/app.cpython-310.pyc" deleted file mode 100644 index beffb5a..0000000 Binary files "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/__pycache__/app.cpython-310.pyc" and /dev/null differ diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/app.py" "b/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/app.py" deleted file mode 100644 index 3ccb6f8..0000000 --- "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/app.py" +++ /dev/null @@ -1,28 +0,0 @@ -from flask import Flask, render_template, url_for - -app = Flask(__name__) - -@app.route('/productdetail') -def productdetail(): - - product = { - 'name': '토끼 키링', - 'seller': '이화연', - 'is_green': True, - 'category': '이화 굿즈', - 'price': '5,000원', - 'short_intro': '토끼 키링', - 'region': '서울 서대문구 이화여대길', - 'status': '새 제품 - 최상', - 'stock': 3, - 'description': '수제 토끼 키링입니다', - 'reviews': [ - {'nickname': 'user1', 'rating': 4, 'content': '귀여워요'}, - {'nickname': 'user2', 'rating': 5, 'content': '마음에 들어요!'} - ] - } - - return render_template('productdetail.html', product=product) - -if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file diff --git "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/static/productdetailStyle.css" "b/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/static/productdetailStyle.css" deleted file mode 100644 index bd48435..0000000 --- "a/\354\203\201\355\222\210 \354\240\225\353\263\264 \354\240\204\354\262\264 \353\246\254\353\267\260/\354\203\201\355\222\210 \354\203\201\354\204\270 \354\241\260\355\232\214/static/productdetailStyle.css" +++ /dev/null @@ -1,156 +0,0 @@ -/* 기본 스타일 */ -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - font-family: Arial, sans-serif; - padding: 20px; - background-color: #f8f8f8; -} - -header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; -} - -header h1 { - font-size: 28px; - color: #4CAF50; -} - -nav { - display: flex; - gap: 20px; -} - -nav a { - text-decoration: none; - color: #333; - font-weight: bold; -} - -.user-info { - display: flex; - align-items: center; - gap: 10px; -} - -.user-info .badge { - background-color: #4CAF50; - color: white; - padding: 5px 10px; - border-radius: 5px; -} - -main { - display: flex; - flex-direction: column; - align-items: center; - margin-top: 40px; -} - -.product-details { - display: flex; - width: 80%; - justify-content: space-between; - background-color: white; - padding: 20px; - border-radius: 10px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); - align-items: stretch; -} - -.left-section, -.right-section { - width: 48%; - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.image-container { - position: relative; - margin-bottom: 10px; -} - -.image-container h2 { - position: absolute; - top: -30px; - left: 10px; - font-size: 20px; - font-weight: bold; -} - -.product-image { - width: 300px; - height: auto; - max-width: 100%; - height: auto; -} - - -.product-info p, -.right-section p { - margin: 5px 0; -} - -.product-info p strong, -.right-section p strong { - font-weight: bold; -} - -.stars { - color: #FFD700; - font-size: 20px; -} - -.reviews { - margin-top: 20px; -} - -.review-list { - margin-top: 10px; -} - -.review { - margin-bottom: 10px; - padding-bottom: 10px; - border-bottom: 1px solid #ddd; -} - -.review-actions { - display: flex; - justify-content: flex-end; - margin-top: 10px; -} - -.more-reviews { - text-decoration: none; - color: #4CAF50; - font-weight: bold; -} - - -.purchase-section { - margin-top: 20px; - display: flex; - justify-content: flex-start; -} - -.purchase-section button { - padding: 15px 30px; - background-color: #4CAF50; - color: white; - border: none; - border-radius: 5px; - cursor: pointer; -} - -.purchase-section button:hover { - background-color: #45a049; -} \ No newline at end of file