Final Project 신세계아이앤씨

달력 일자 선택시 7일이내의 유통기한 임박상품 불러오기 ( 리액트, 스프링부트 )

pjh8838 2024. 7. 30. 16:20
반응형

 

프론트 ( 리액트 )

 

달력

const [selectedDate, setSelectedDate] = useState(null);

 

onClick={() => handleDateClick(day)}

  // 날짜를 클릭했을 때 실행되는 함수
  const handleDateClick = (date) => {
    setSelectedDate(date); // 선택된 날짜 상태 업데이트
    onDateSelect(date); // 부모 컴포넌트로 선택된 날짜 전달
  };

클릭이벤트로 일자 선택시 가져오는 이벤트 만듬

 

 

메인페이지 ( 달력 일자 선택 시 해당하는 데이터 조회 )

<Calendar onDateSelect={handleDateSelect} />

 

const handleDateSelect = (selectedDate) => {
    // 날짜가 어떤 형식으로 들어오는지 검증
    const formattedDate = format(selectedDate, 'yyyy-MM-dd');
    console.log("Formatted date for API:", formattedDate);
    axios.get(`http://localhost:8090/traders/stock?date=${formattedDate}`)
    // axios.get(`http://localhost:8090/traders/stock?date=${format(selectedDate, 'yyyy-MM-dd')}`)
      .then(response => {
        //data가 들어오는지 검증
        console.log("API:", response.data);

        // 선택된 날짜로부터 7일 이내에 유통기한이 끝나는 상품만 필터링
        const expiringProducts = response.data.filter(stock => {
 
          // console.log("date변환전 : " + JSON.stringify(stock));
          // stock을 콘솔로 찍었을때 object라고만 떠서 JSON.stringify로 문자열로 변환후 데이터를 확인

          console.log("date변환전 : " + stock.expdate);
          // stock에 날짜가 expdate로 들어오고 있었음

          //날짜 변환
          const expirationDate = new Date(stock.expdate);
          console.log("test:" + expirationDate);
         
          const daysDifference = (expirationDate - selectedDate) / (1000 * 60 * 60 * 24);
                                                 //1000밀리초, 60초, 60분, 24시간 = 86,400,000 (날짜간의 차이를 계산할때 정확하게하기 위해)
          return daysDifference >= -7 && daysDifference <= 1;
        });
        setStock(expiringProducts);
        console.log(expiringProducts);
        console.log(response.data);
      })
      .catch(error => {
        console.error('There was an error fetching the stock!', error);
      });
  };

 

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------

 

달력 전체코드

import React, { useState } from 'react';
import { format, addMonths, subMonths, startOfMonth, endOfMonth, startOfWeek, addDays, getDay, getWeek, isSameMonth } from 'date-fns';
import '../components/Calendar.css';

const Calendar = ({ onDateSelect }) => {
  const [currentDate, setCurrentDate] = useState(new Date()); // 현재 날짜를 저장하는 상태
  const [selectedDate, setSelectedDate] = useState(null); // 선택된 날짜를 저장하는 상태

  // 한국어로 된 월 이름을 반환하는 함수
  const getKoreanMonth = (date) => {
    const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
    return months[date.getMonth()];
  };

  // 해당 월의 날짜들을 반환하는 함수
  const getDaysInMonth = (date) => {
    const start = startOfWeek(startOfMonth(date), { weekStartsOn: 1 });
    let days = [];
    for (let i = 0; i < 35; i++) { // 5주(7일*5=35일)를 표현하기 위해 35일을 계산
      days.push(addDays(start, i));
    }
    return days;
  };

  // 특정 날짜가 공휴일인지 확인하는 함수
  const isHoliday = (date) => {
    const dayOfWeek = getDay(date); // 요일을 가져옴 (0: 일요일, 1: 월요일, ...)
    const weekOfMonth = getWeek(date) - getWeek(startOfMonth(date)); // 해당 월의 몇 번째 주인지 계산
    return dayOfWeek === 0 && (weekOfMonth === 2 || weekOfMonth === 4); // 둘째 주, 넷째 주 일요일을 공휴일로 간주
  };

  const days = getDaysInMonth(currentDate); // 현재 월의 날짜들 가져오기

  // 이전 달로 이동하는 함수
  const onPrevMonth = () => setCurrentDate(subMonths(currentDate, 1));
  // 다음 달로 이동하는 함수
  const onNextMonth = () => setCurrentDate(addMonths(currentDate, 1));

  // 날짜를 클릭했을 때 실행되는 함수
  const handleDateClick = (date) => {
    setSelectedDate(date); // 선택된 날짜 상태 업데이트
    onDateSelect(date); // 부모 컴포넌트로 선택된 날짜 전달
  };

  return (
    <div className="calendar">
      <div className="calendar-header">
        <button onClick={onPrevMonth} className='cal_btnP'>&lt;</button> {/* 이전 달 버튼 */}
        <h2>{getKoreanMonth(currentDate)} {format(currentDate, 'yyyy')}</h2> {/* 현재 월과 연도 표시 */}
        <button onClick={onNextMonth} className='cal_btnN'>&gt;</button> {/* 다음 달 버튼 */}
      </div>
      <div className="calendar-body">
        <div className="calendar-week-days">
          {['월', '화', '수', '목', '금', '토', '일'].map(day => (
            <div key={day} className="week-day">{day}</div> // 요일 표시
          ))}
        </div>
        <div className="calendar-days">
          {days.map(day => (
            <div
              key={day.toString()}
              className={`calendar-day
                ${!isSameMonth(day, currentDate) ? 'other-month' : ''} // 다른 달의 날짜 표시
                ${isHoliday(day) ? 'holiday' : ''} // 공휴일 표시
                ${selectedDate && day.toDateString() === selectedDate.toDateString() ? 'selected' : ''}` // 선택된 날짜 표시
              }
              onClick={() => handleDateClick(day)}
            >
              {format(day, 'd')} {/* 날짜 숫자 표시 */}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

export default Calendar;

 

 

메인  페이지 전체코드

import React, { useState, useEffect, useRef } from 'react';
import main from '../pages/Main.module.css';
import Calendar from '../components/Calendar';
import axios from 'axios';
import { format } from 'date-fns';

function Main() {
  const [goods, setGoods] = useState([]);
  const [searchGoods, setSearchGoods] = useState('');
  const [stock, setStock] = useState([]);
  const [stockk, setStockk] = useState([]); // 재고부족 상품 상태
  const [selectedGoods, setSelectedGoods] = useState([]);
  const prevSelectedDate = useRef(null);  // 이전에 선택한 날짜를 추적하는 ref
  const [expiringMessage, setExpiringMessage] = useState('');  // 메시지를 저장할 상태 추가

  useEffect(() => {
    axios.get('http://localhost:8090/traders/home')
      .then(response => {
        setGoods(response.data);
      })
      .catch(error => {
        console.error('goods상품 조회 불가', error);
      });

    // 재고부족 상품 리스트 조회
    axios.get('http://localhost:8090/traders/stock')
      .then(response => {
        const shortage = response.data.filter(stock => stock.stockquantity <= 10).sort((a, b) => a.stockquantity - b.stockquantity);
        setStockk(shortage);
      })
      .catch(error => {
        console.error('There was an error fetching the goods!', error);
      });
  }, []);

  const handleSearch = (event) => {
    event.preventDefault();
    axios.get(`http://localhost:8090/traders/home/${searchGoods}`)
      .then(response => {
        setGoods(response.data);
      })
      .catch(error => {
        console.error('goods상품 검색 불가', error);
      });
  };

  const handleDateSelect = (selectedDate) => {
    // 선택한 날짜가 이전에 선택한 날짜와 다른 경우에만 실행
    if (prevSelectedDate.current && prevSelectedDate.current.getTime() === selectedDate.getTime()) {
      return;
    }
    prevSelectedDate.current = selectedDate;

    // 날짜가 어떤 형식으로 들어오는지 검증
    const formattedDate = format(selectedDate, 'yyyy-MM-dd');
    axios.get(`http://localhost:8090/traders/stock?date=${formattedDate}`)
      .then(response => {
        //data가 들어오는지 검증
        console.log("API:", response.data);

        // 선택된 날짜로부터 7일 이내에 유통기한이 끝나는 상품만 필터링
        const expiringProducts = response.data.filter(stock => {
          // console.log("date변환전 : " + JSON.stringify(stock));
          // stock을 콘솔로 찍었을때 object라고만 떠서 JSON.stringify로 문자열로 변환후 데이터를 확인

          console.log("date변환전 : " + stock.expdate);
          //stock에 날짜가 expdate로 들어오고 있었음
          const expirationDate = new Date(stock.expdate); //유통기한
          console.log("test:" + expirationDate);
          const daysDifference = (expirationDate - selectedDate) / (1000 * 60 * 60 * 24);
          //1000밀리초, 60초, 60분, 24시간 = 86,400,000 (날짜간의 차이를 계산할때 정확하게하기 위해)
          console.log("tt:" + daysDifference);
          return daysDifference >= 0 && daysDifference <= 7;
        });
        if (expiringProducts.length > 0) {
          setStock(expiringProducts);
          console.log(expiringProducts);
          setExpiringMessage('');
        } else {
          setStock([]);
          setExpiringMessage('7일 이내에 유통기한이 임박한 상품이 없습니다.');
        }
      })
      .catch(error => {
        console.error('There was an error fetching the stock!', error);
      });
  };

  // 개별 선택 체크
  const handleSelect = (gcode) => {
    setSelectedGoods(prevSelectedGoods =>
      prevSelectedGoods.includes(gcode)
        ? prevSelectedGoods.filter(code => code !== gcode)
        : [...prevSelectedGoods, gcode]
    );
  };

  //전체 선택 체크
  const handleSelectAll = (event) => {
    if (event.target.checked) {
      setSelectedGoods(goods.map(item => item.gcode));
    } else {
      setSelectedGoods([]);
    }
  };

  return (
    <div className={main.Main}>
      <div className={main.goods_page}>
        <div className={main.leftSection}>
          <form onSubmit={handleSearch} id='goods-form'>
            <input type='search'
              name='goods_search'
              placeholder='제품코드, 카테고리명, 상품명 검색'
              className={main.inputGoodsSearch}
              value={searchGoods}
              onChange={(e) => setSearchGoods(e.target.value)} />
            <button type="submit" className={main.btnGoodsSearch}>검색</button>
          </form>
          <div className={main.goodsList}>
            <table className={main.goodsTable}>
              <thead>
                <tr>
                  <th style={{ width: '50px' }}>
                    <input type="checkbox"
                      onChange={handleSelectAll}
                      checked={selectedGoods.length === goods.length} /></th>
                  <th style={{ width: '65px' }}>제품번호</th>
                  <th>제품코드</th>
                  <th>카테고리</th>
                  <th>상품명(단위)</th>
                  <th>가격</th>
                </tr>
              </thead>
              <tbody>
                {goods.map((goods, index) => (
                  <tr key={index} className={main.goodsItem}>
                    <td><input type="checkbox"
                      checked={selectedGoods.includes(goods.gcode)}
                      onChange={() => handleSelect(goods.gcode)} /></td>
                    <td>{index + 1}</td>
                    <td>{goods.gcode}</td>
                    <td>{goods.gcategory}</td>
                    <td>{goods.gname}</td>
                    <td>{goods.gcostprice.toLocaleString('ko-KR')}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <button className={main.orBtn} onClick={handleOrder}>발주하기</button>

          <div className={main.events}>
            이벤트 슬라이드
          </div>
        </div>
      </div>

      <div className={main.rightsection}>
        <div className={main.locCalender}>
          <Calendar onDateSelect={handleDateSelect} />
        </div>
        <div className={main.tableLabel}>
          <div className={main.tableLabel2}>유통기한 임박 상품 리스트</div>
          <div className={main.tableLabel3}>재고 부족 상품 리스트</div>
        </div>
        <div className={main.rightSectionBox}>
          <div className={main.disuseList}>
            <table className={main.disuseTable}>
              <thead>
                <tr>
                  <th style={{ width: '12%' }}>번호</th>
                  <th style={{ width: '28%' }}>제품코드</th>
                  <th style={{ width: '35%' }}>상품명</th>
                  <th style={{ width: '25%' }}>유통기한</th>
                </tr>
              </thead>
              <tbody>
                {stock.length > 0 ? (
                  stock.map((stock, index) => (
                    <tr key={index} className={main.stockItem}>
                      <td>{index + 1}</td>
                      <td>{stock.goods.gcode}</td>
                      <td>{stock.goods.gname}</td>
                      <td>{stock.expdate}</td>
                    </tr>
                  ))
                ) : (
                  <tr>
                    <td colSpan="4">{expiringMessage}</td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>

          <div className={main.stockList}>
            <table className={main.stockTable}>
              <thead>
                <tr>
                  <th style={{ width: '12%' }}>번호</th>
                  <th style={{ width: '28%' }}>제품코드</th>
                  <th style={{ width: '35%' }}>상품명</th>
                  <th style={{ width: '12%' }}>수량</th>
                </tr>
              </thead>
              <tbody>
                {stockk.map((stock, index) => (
                  <tr key={index} className={main.stockItem}>
                    <td>{index + 1}</td>
                    <td>{stock.goods.gcode}</td>
                    <td>{stock.goods.gname}</td>
                    <td>{stock.stockquantity}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  );
}

export default Main;

 

 

 

결과

728x90
반응형