diff --git a/client/images/icon-x128.png b/client/assets/icon-x128.png similarity index 100% rename from client/images/icon-x128.png rename to client/assets/icon-x128.png diff --git a/client/images/icon-x192.png b/client/assets/icon-x192.png similarity index 100% rename from client/images/icon-x192.png rename to client/assets/icon-x192.png diff --git a/client/images/icon-x384.png b/client/assets/icon-x384.png similarity index 100% rename from client/images/icon-x384.png rename to client/assets/icon-x384.png diff --git a/client/images/icon-x512.png b/client/assets/icon-x512.png similarity index 100% rename from client/images/icon-x512.png rename to client/assets/icon-x512.png diff --git a/client/manifest.json b/client/manifest.json index 9b6bbdc..1f2704f 100644 --- a/client/manifest.json +++ b/client/manifest.json @@ -3,28 +3,28 @@ "short_name": "Amatta", "icons": [ { - "src": "https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x128.png", + "src": "assets/icon-x128.png", "sizes": "128x128", "type": "image/png" }, { - "src": "https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x192.png", + "src": "assets/icon-x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, { - "src": "https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x384.png", + "src": "assets/icon-x384.png", "sizes": "384x384", "type": "image/png" }, { - "src": "https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x512.png", + "src": "assets/icon-x512.png", "sizes": "512x512", "type": "image/png" } ], - "start_url": "https://amatta.site/", + "start_url": "/", "display": "fullscreen", "background_color": "#92b8b1", "theme_color": "#ffffff" diff --git a/client/service-worker.js b/client/service-worker.js index 7667a9c..5553c5b 100644 --- a/client/service-worker.js +++ b/client/service-worker.js @@ -1,61 +1,49 @@ -importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js'); - -const CACHE = 'cache-offline-page'; -const offlineFallbackPage = 'offline.html'; - -self.addEventListener('message', (event) => { - if (event.data && event.data.type === 'SKIP_WAITING') { - self.skipWaiting(); - } -}); +const CACHE_NAME = 'my-pwa-cache'; +const urlsToCache = ['/', '/offline.html']; self.addEventListener('install', (event) => { - event.waitUntil( - caches - .open(CACHE) - .then((cache) => - cache.addAll([ - 'https://amatta.site/', - './offline.html', - 'https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x128.png', - 'https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x192.png', - 'https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x384.png', - 'https://amatta-icons.s3.ap-northeast-2.amazonaws.com/app/icon-x512.png', - ]), - ), - ); + event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(urlsToCache))); }); -if (workbox.navigationPreload.isSupported()) { - workbox.navigationPreload.enable(); -} - -workbox.routing.registerRoute( - new RegExp('/*'), - new workbox.strategies.StaleWhileRevalidate({ - cacheName: CACHE, - }), -); - self.addEventListener('fetch', (event) => { - if (event.request.mode === 'navigate') { - event.respondWith( - (async () => { - try { - const preloadResp = await event.preloadResponse; - - if (preloadResp) { - return preloadResp; + event.respondWith( + caches + .match(event.request) + .then((response) => { + if (response) { + return response; + } + return fetch(event.request).then((response) => { + if (response.status === 404) { + return caches.match('/offline.html'); } + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + return response; + }); + }) + .catch(() => { + return caches.match('/offline.html'); + }), + ); +}); - const networkResp = await fetch(event.request); - return networkResp; - } catch (error) { - const cache = await caches.open(CACHE); - const cachedResp = await cache.match(offlineFallbackPage); - return cachedResp; - } - })(), - ); - } +self.addEventListener('beforeinstallprompt', (event) => { + event.preventDefault(); + const promptEvent = event; + const installButton = document.createElement('button'); + installButton.textContent = 'Add to Home Screen'; + document.body.appendChild(installButton); + installButton.addEventListener('click', () => { + promptEvent.prompt(); + promptEvent.userChoice.then((choiceResult) => { + if (choiceResult.outcome === 'accepted') { + console.log('User accepted the install prompt'); + } else { + console.log('User dismissed the install prompt'); + } + }); + }); }); diff --git a/client/src/apis/card.js b/client/src/apis/card.js index df4b740..b7819c4 100644 --- a/client/src/apis/card.js +++ b/client/src/apis/card.js @@ -1,8 +1,8 @@ import client from './client'; -export const getCardList = async () => { +export const getCardList = async (query = '') => { try { - const response = await client.get(`gifticon/test`); + const response = await client.get(`gifticon/test?keyword=${query}`); return response.data; } catch (e) { diff --git a/client/src/apis/client.js b/client/src/apis/client.js index 133622f..412e6ec 100644 --- a/client/src/apis/client.js +++ b/client/src/apis/client.js @@ -1,5 +1,7 @@ import axios from 'axios'; import { SERVER_URL } from '@/constants/constant'; +import { navigate } from '@/core/router'; +import { notification } from '@/components/common'; const baseURL = SERVER_URL.API; @@ -9,4 +11,26 @@ const client = axios.create({ client.defaults.withCredentials = true; +client.interceptors.response.use( + (response) => response, + (error) => { + if (error.response && error.response.status) { + switch (error.response.status) { + case 401: + navigate('/'); + notification('로그인이 필요합니다.', 'login')(); + return new Promise(() => {}); + case 400: + notification(`${error.response.data}`, 'login')(); + return new Promise(() => {}); + default: + return Promise.reject(error); + } + } else if (error.code === 'ERR_NETWORK') { + notification('올바르지 않은 이미지 형식입니다.', 'login')(); + } + return Promise.reject(error); + }, +); + export default client; diff --git a/client/src/components/auth/login.js b/client/src/components/auth/login.js index 4b888f6..b0e1099 100644 --- a/client/src/components/auth/login.js +++ b/client/src/components/auth/login.js @@ -43,7 +43,6 @@ const login = () => { const submitData = async (e) => { e.stopPropagation(); e.preventDefault(); - console.log('뭔데'); await loginU({ email: 'test@test.com', password: 'testPassword' }); // await loginU(userData); diff --git a/client/src/components/common/header.js b/client/src/components/common/header.js index 4b79d8c..1765aff 100644 --- a/client/src/components/common/header.js +++ b/client/src/components/common/header.js @@ -4,6 +4,7 @@ import { logoutU } from '@/apis/auth'; import { _ } from '@/utils/customFx'; import { IO, $ } from '@/utils'; import { sideMenu, modal } from '@/components/common'; + // search-button // const header = (props) => { const { color, label, path } = props; @@ -14,6 +15,7 @@ const header = (props) => { const mintTemp = ` amatta-small-logo +
search-button
@@ -108,12 +110,24 @@ const header = (props) => { const setEvent = (type, fn) => (target) => IO.of(eventTrigger(type, target, fn)); const findTarget = (child, parent) => () => $.qs(child, parent); const handleClickSearchIcon = (target) => (e) => { - target.style.filter = - 'invert(0%) sepia(0%) saturate(7445%) hue-rotate(197deg) brightness(86%) contrast(93%)'; - target.style.transform = 'translateY(50px)'; + const inputTarget = $.qs('.search-card-input'); + if (!target.classList.contains('searching')) { + target.style.filter = + 'invert(66%) sepia(2%) saturate(19%) hue-rotate(334deg) brightness(97%) contrast(82%)'; + inputTarget.style.animation = 'search 0.5s ease-in-out forwards'; + target.classList.add('searching'); + } else { + target.style.filter = + 'invert(100%) sepia(0%) saturate(0%) hue-rotate(113deg) brightness(104%) contrast(101%)'; + target.classList.remove('searching'); + inputTarget.style.opacity = '0'; + inputTarget.style.animation = ''; + } }; const addEvents = (target) => { + if (!$.qs('.search-button')) return; + IO.of(findTarget('.search-button', target)) .chain(setEvent('click', handleClickSearchIcon($.qs('.search-button')))) .run(); @@ -135,7 +149,7 @@ const header = (props) => { $.append($.qs('.header-main')), () => $.qs('.trigger'), $.on('click', openMenuEvent)); - //addEvents(document); + addEvents(document); } return appendHeader; }; diff --git a/client/src/components/main/cardSearch.js b/client/src/components/main/cardSearch.js deleted file mode 100644 index deb3aac..0000000 --- a/client/src/components/main/cardSearch.js +++ /dev/null @@ -1,29 +0,0 @@ -import { SERVER_URL } from '@/constants/constant'; -import { $ } from '@/utils'; -import { _ } from '@/utils/customFx'; - -const searchCard = () => { - const SEARCH_ICON_URL = `${SERVER_URL.IMG}icon/search.svg`; - - const searchTemp = ` -
-
-
전체
-
상품별
-
매장별
-
- -
-`; - - // prettier-ignore - const appendSearchCard = () => { - // const target = $.qs('.main-card-article'); - // _.go( - // searchTemp, - // (fragment) => target.insertAdjacentHTML('beforebegin', searchTemp)); - } - - return appendSearchCard; -}; -export default searchCard; diff --git a/client/src/components/main/index.js b/client/src/components/main/index.js index 3f2c28b..1a2f0af 100644 --- a/client/src/components/main/index.js +++ b/client/src/components/main/index.js @@ -1,2 +1 @@ export { default as cardDetail } from './cardDetail'; -export { default as searchCard } from './cardSearch'; diff --git a/client/src/main.js b/client/src/main.js index f50f5cc..0bcab0f 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -43,7 +43,7 @@ const messaging = getMessaging(app); // if ('serviceWorker' in navigator) { // window.addEventListener('load', () => { // navigator.serviceWorker -// .register('../service-worker.js') +// .register('./service-worker.js') // .then((reg) => { // console.log('Service worker registered!!!!!!!.', reg); // }) @@ -53,7 +53,7 @@ const messaging = getMessaging(app); // }); // } -navigator.serviceWorker.register('../firebase-messaging-sw.js').then((res) => { +navigator.serviceWorker.register('./firebase-messaging-sw.js').then((res) => { onMessage(messaging, (payload) => { const option = { body: payload.notification.body, diff --git a/client/src/pages/MainPage.js b/client/src/pages/MainPage.js index b2df997..b68a25d 100644 --- a/client/src/pages/MainPage.js +++ b/client/src/pages/MainPage.js @@ -1,6 +1,6 @@ import JsBarcode from 'jsbarcode'; import { SERVER_URL } from '@/constants/constant'; -import { cardDetail, searchCard } from '@/components/main'; +import { cardDetail } from '@/components/main'; import { dropdownMenu, header, notification } from '@/components/common'; import { IO, $, slider } from '@/utils'; import { _ } from '@/utils/customFx'; @@ -80,7 +80,15 @@ const handleSortClick = ({ target }, dropdownSection) => { dropdownSection.innerHTML = dropdownMenu(sortOption); $.on('click', toggleDropdown)($.qs('.main-dropdown-button')); dropdownSection.classList.remove('drop'); - // changeCardData(cardDatas); +}; + +const handleClickSearchIcon = (target) => async (e) => { + e.preventDefault(); + const inputTarget = $.qs('.search-card-input'); + + if (target.classList.contains('searching')) return; + const newData = await getCardList(inputTarget.value); + navigateMain(newData); }; const toggleDropdown = () => { @@ -150,6 +158,10 @@ const addEvents = (target) => { IO.of(findTarget('.card-lists', target)) .chain(setEvent('click', handleClickOneCard)) .run(); + + IO.of(findTarget('.search-button', target)) + .chain(setEvent('click', handleClickSearchIcon($.qs('.search-button')))) + .run(); }; const handleClickOneCard = ({ target }) => { @@ -175,13 +187,13 @@ MainPage.render = () => $.replace($.qs('#root'))); // prettier-ignore -const navigateMain = async () => { - setCardDatas(await getCardList()); +const navigateMain = async (newData = '') => { + newData = '/card' && (newData = ''); + setCardDatas(await getCardList(newData)); dateComparison(); - + _.go( MainPage.render(), - addEvents, slider(), () => $.qsa('.mark-used-button'), makeUsedState, @@ -192,10 +204,10 @@ const navigateMain = async () => { () => $.qs('.list-card-button'), $.on('click', switchLayout), () => MainPage.handleClickaddCard()); - - header({color: 'mint'})(); - createBarcode(); - searchCard()(); + + createBarcode(); + header({color: 'mint'})(); + addEvents(document); } export default navigateMain; diff --git a/client/src/pages/PostPage.js b/client/src/pages/PostPage.js index dfadda7..d559b22 100644 --- a/client/src/pages/PostPage.js +++ b/client/src/pages/PostPage.js @@ -109,7 +109,6 @@ const uploadImg = (file) => { const imageData = { gifticonBase64: base64URL, format: imageType.replace('data:image/', '') }; const list = []; const response = await sendImage(imageData); - console.log(response); response.images[0].fields.forEach((field) => { if (field.inferConfidence >= 0.92) { const object = {}; @@ -143,7 +142,6 @@ const uploadImg = (file) => { diff <= 10 ? (acc[acc.length - 1] += cur.text) : acc.push(cur.text); return acc; }, []); - console.log(lastArr); const { itemName, brandName, expiresAt, barcode } = await sendImageInfo({ texts: lastArr }); changeHeader('white'); diff --git a/client/src/styles/header.scss b/client/src/styles/header.scss index 8069f77..68361e6 100644 --- a/client/src/styles/header.scss +++ b/client/src/styles/header.scss @@ -4,7 +4,6 @@ height: 4.5rem; background-color: $primary-color; position: fixed; - // position: relative; display: flex; justify-content: space-between; align-items: center; @@ -43,9 +42,10 @@ } .search-button { - margin-right: 4rem; + margin-right: calc(2.5rem + 20px); height: 1.75rem; width: 1.75rem; + transition: all 1s; filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(113deg) brightness(104%) contrast(101%); } @@ -55,7 +55,6 @@ justify-content: space-around; align-items: flex-end; margin-left: 1rem; - // display: block; width: 1.75rem; height: 1.25rem; right: 20px; @@ -99,50 +98,26 @@ } } } +} - /* Header - Trigger */ - // .trigger { - // display: flex; - // flex-direction: column; - // justify-content: space-around; - // width: 1.75rem; - // height: 1.75rem; - // margin-left: 1rem; - // align-items: flex-end; - // position: absolute; - // z-index: 11; - // //right: 0.5rem; - - // span { - // height: 0.175rem; - // width: 100%; - // background-color: $white-color; - // transition: 0.5s; - - // &:nth-child(2) { - // width: 80%; - // } - // } - - // &.active { - // align-items: center; - // justify-content: center; - // z-index: 11; - - // span { - // width: 70%; - // background-color: $primary-color; +.search-card-input { + border: 1px solid $gray400; + border-radius: 1.5rem; + height: 2.75rem; + position: absolute; + right: calc(1.75rem + 20px); + padding: 0 1rem; + opacity: 0; + outline: none; +} - // &:nth-child(1) { - // transform: rotate(45deg); - // } - // &:nth-child(2) { - // display: none; - // } - // &:nth-child(3) { - // transform: rotate(-45deg); - // } - // } - // } - // } +@keyframes search { + from { + opacity: 0; + width: 0vw; + } + to { + opacity: 1; + width: 50vw; + } } diff --git a/client/src/styles/main.scss b/client/src/styles/main.scss index bf46ce2..2520d5c 100644 --- a/client/src/styles/main.scss +++ b/client/src/styles/main.scss @@ -534,30 +534,3 @@ } } } - -.search-card { - width: 100vw; - padding-top: 4.5rem; - position: fixed; - display: flex; - justify-content: center; - align-items: center; - background-color: $white-color; - border-radius: 5rem; - height: 2rem; - - .search-filter { - display: flex; - flex-direction: column; - width: 15%; - } - - input { - width: 80%; - outline: none; - } - - .search-icon { - width: 2rem; - } -} diff --git a/client/src/utils/domFx.js b/client/src/utils/domFx.js index a9111a3..12bd8f7 100644 --- a/client/src/utils/domFx.js +++ b/client/src/utils/domFx.js @@ -35,7 +35,6 @@ $.prepend = _.curry((child, parent) => { $.append = _.curry((parent, child) => { parent.appendChild(child); - // console.log(parent, child); return child; }); diff --git a/client/vite.config.js b/client/vite.config.js index 8a846c7..a98624a 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -1,7 +1,17 @@ import { defineConfig } from 'vite'; +import path from 'path'; export default defineConfig({ resolve: { alias: [{ find: '@', replacement: '/src' }], }, + build: { + outDir: 'dist', + rollupOptions: { + input: { + main: path.resolve(__dirname, 'index.html'), + firebase: path.resolve(__dirname, 'firebase-messaging-sw.js'), + }, + }, + }, });