Showing
42 changed files
with
1266 additions
and
242 deletions
.gitignore
0 → 100644
1 | +# Logs | ||
2 | +logs | ||
3 | +*.log | ||
4 | +npm-debug.log* | ||
5 | +yarn-debug.log* | ||
6 | +yarn-error.log* | ||
7 | +lerna-debug.log* | ||
8 | + | ||
9 | +# Diagnostic reports (https://nodejs.org/api/report.html) | ||
10 | +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | ||
11 | + | ||
12 | +# Runtime data | ||
13 | +pids | ||
14 | +*.pid | ||
15 | +*.seed | ||
16 | +*.pid.lock | ||
17 | + | ||
18 | +# Directory for instrumented libs generated by jscoverage/JSCover | ||
19 | +lib-cov | ||
20 | + | ||
21 | +# Coverage directory used by tools like istanbul | ||
22 | +coverage | ||
23 | +*.lcov | ||
24 | + | ||
25 | +# nyc test coverage | ||
26 | +.nyc_output | ||
27 | + | ||
28 | +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | ||
29 | +.grunt | ||
30 | + | ||
31 | +# Bower dependency directory (https://bower.io/) | ||
32 | +bower_components | ||
33 | + | ||
34 | +# node-waf configuration | ||
35 | +.lock-wscript | ||
36 | + | ||
37 | +# Compiled binary addons (https://nodejs.org/api/addons.html) | ||
38 | +build/Release | ||
39 | + | ||
40 | +# Dependency directories | ||
41 | +node_modules/ | ||
42 | +jspm_packages/ | ||
43 | + | ||
44 | +# TypeScript v1 declaration files | ||
45 | +typings/ | ||
46 | + | ||
47 | +# TypeScript cache | ||
48 | +*.tsbuildinfo | ||
49 | + | ||
50 | +# Optional npm cache directory | ||
51 | +.npm | ||
52 | + | ||
53 | +# Optional eslint cache | ||
54 | +.eslintcache | ||
55 | + | ||
56 | +# Microbundle cache | ||
57 | +.rpt2_cache/ | ||
58 | +.rts2_cache_cjs/ | ||
59 | +.rts2_cache_es/ | ||
60 | +.rts2_cache_umd/ | ||
61 | + | ||
62 | +# Optional REPL history | ||
63 | +.node_repl_history | ||
64 | + | ||
65 | +# Output of 'npm pack' | ||
66 | +*.tgz | ||
67 | + | ||
68 | +# Yarn Integrity file | ||
69 | +.yarn-integrity | ||
70 | + | ||
71 | +# dotenv environment variables file | ||
72 | +.env | ||
73 | +.env.test | ||
74 | + | ||
75 | +# parcel-bundler cache (https://parceljs.org/) | ||
76 | +.cache | ||
77 | + | ||
78 | +# Next.js build output | ||
79 | +.next | ||
80 | + | ||
81 | +# Nuxt.js build / generate output | ||
82 | +.nuxt | ||
83 | +dist | ||
84 | + | ||
85 | +# Gatsby files | ||
86 | +.cache/ | ||
87 | +# Comment in the public line in if your project uses Gatsby and *not* Next.js | ||
88 | +# https://nextjs.org/blog/next-9-1#public-directory-support | ||
89 | +# public | ||
90 | + | ||
91 | +# vuepress build output | ||
92 | +.vuepress/dist | ||
93 | + | ||
94 | +# Serverless directories | ||
95 | +.serverless/ | ||
96 | + | ||
97 | +# FuseBox cache | ||
98 | +.fusebox/ | ||
99 | + | ||
100 | +# DynamoDB Local files | ||
101 | +.dynamodb/ | ||
102 | + | ||
103 | +# TernJS port file | ||
104 | +.tern-port |
LICENSE
0 → 100644
1 | +This is free and unencumbered software released into the public domain. | ||
2 | + | ||
3 | +Anyone is free to copy, modify, publish, use, compile, sell, or | ||
4 | +distribute this software, either in source code form or as a compiled | ||
5 | +binary, for any purpose, commercial or non-commercial, and by any | ||
6 | +means. | ||
7 | + | ||
8 | +In jurisdictions that recognize copyright laws, the author or authors | ||
9 | +of this software dedicate any and all copyright interest in the | ||
10 | +software to the public domain. We make this dedication for the benefit | ||
11 | +of the public at large and to the detriment of our heirs and | ||
12 | +successors. We intend this dedication to be an overt act of | ||
13 | +relinquishment in perpetuity of all present and future rights to this | ||
14 | +software under copyright law. | ||
15 | + | ||
16 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
17 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
18 | +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
19 | +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
20 | +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
21 | +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
22 | +OTHER DEALINGS IN THE SOFTWARE. | ||
23 | + | ||
24 | +For more information, please refer to <https://unlicense.org> |
... | @@ -16,6 +16,8 @@ app.use(express.urlencoded({ extended: true })) | ... | @@ -16,6 +16,8 @@ app.use(express.urlencoded({ extended: true })) |
16 | // 터미널창 연결방법 mongo 'mongodb+srv://hellowhales:qogudtjr`12@cluster0.7gz7l.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'; | 16 | // 터미널창 연결방법 mongo 'mongodb+srv://hellowhales:qogudtjr`12@cluster0.7gz7l.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'; |
17 | // db.festivals.find({"title":"가무악극으로 만나는 토요 상설공연"}) 검색방법 | 17 | // db.festivals.find({"title":"가무악극으로 만나는 토요 상설공연"}) 검색방법 |
18 | 18 | ||
19 | +//https://www.mongodb.com/try/download/database-tools?tck=docs_databasetools 여기서 mongoexport 실행을 위한 tool 다운받아서 Program files/mongodb/bin 폴더 안에 넣어줘야 함 | ||
20 | + | ||
19 | MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB와 연결시키기 | 21 | MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB와 연결시키기 |
20 | if (error) return console.log(error); | 22 | if (error) return console.log(error); |
21 | db = client.db('myFirstDatabase'); // 클러스터의 데이터베이스를 db변수에 저장 | 23 | db = client.db('myFirstDatabase'); // 클러스터의 데이터베이스를 db변수에 저장 |
... | @@ -27,14 +29,21 @@ MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB | ... | @@ -27,14 +29,21 @@ MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB |
27 | app.get('/festivalList', (req, res) => { // localhost:3000/festivalList 입력하면 list.ejs에 저장한 형식대로 정보 불러와짐 | 29 | app.get('/festivalList', (req, res) => { // localhost:3000/festivalList 입력하면 list.ejs에 저장한 형식대로 정보 불러와짐 |
28 | //디비에 저장된 festivals 라는 collection안의 데이터(제목 또는 내용 등)를 꺼내기 | 30 | //디비에 저장된 festivals 라는 collection안의 데이터(제목 또는 내용 등)를 꺼내기 |
29 | db.collection('festivals').find().toArray((err, rslt) => { //DB에서 데이터를 찾음 festivals라는 collection안의 데이터를 꺼내게 됨 | 31 | db.collection('festivals').find().toArray((err, rslt) => { //DB에서 데이터를 찾음 festivals라는 collection안의 데이터를 꺼내게 됨 |
32 | + if (err) throw err; | ||
30 | console.log(rslt); | 33 | console.log(rslt); |
31 | res.render('list.ejs', { posts: rslt }); // 찾은 데이터를 ejs 파일에 넣음 | 34 | res.render('list.ejs', { posts: rslt }); // 찾은 데이터를 ejs 파일에 넣음 |
32 | }); | 35 | }); |
33 | }); | 36 | }); |
34 | 37 | ||
35 | 38 | ||
39 | + | ||
40 | +// 서버 연결하고 mongoexport 설치 후 mongoexport -d myFirstDatabase -c festivals -o festivalList.json /pretty 'mongodb+srv://hellowhales:qogudtjr`12@cluster0.7gz7l.mongodb.net/myFirstDatabase?retryWrites=true&w=majority' 명령 | ||
41 | +// 이용해서 myFirstDatabase라는 Db 안에서 festivals라는 collection 안의 데이터를 festivalList.json 파일로 export 해옴. | ||
42 | + | ||
43 | + | ||
36 | var indexRouter = require('./routes/index'); | 44 | var indexRouter = require('./routes/index'); |
37 | var usersRouter = require('./routes/users'); | 45 | var usersRouter = require('./routes/users'); |
46 | +const { mongo } = require('mongoose'); | ||
38 | 47 | ||
39 | 48 | ||
40 | 49 | ||
... | @@ -72,17 +81,6 @@ app.use(function (err, req, res, next) { | ... | @@ -72,17 +81,6 @@ app.use(function (err, req, res, next) { |
72 | 81 | ||
73 | 82 | ||
74 | 83 | ||
75 | -/* | ||
76 | - | ||
77 | -app.get('*', function (req, res) { | ||
78 | - | ||
79 | - //DB에서 json형태의 데이터를 유저가 접속하자마자 바로 불러와서 frontend에 뿌려주기 | ||
80 | - // 렌더링할 때 frontend로 변수 뿌려주기 | ||
81 | - res.render("asd.html"); | ||
82 | - | ||
83 | -}); | ||
84 | -*/ | ||
85 | - | ||
86 | 84 | ||
87 | module.exports = app; | 85 | module.exports = app; |
88 | 86 | ... | ... |
app2.js
deleted
100644 → 0
1 | -var createError = require('http-errors'); | ||
2 | -var express = require('express'); | ||
3 | -var path = require('path'); | ||
4 | -var cookieParser = require('cookie-parser'); | ||
5 | -var logger = require('morgan'); | ||
6 | -var app = express(); | ||
7 | -const mongodb = require('mongodb'); | ||
8 | -const MongoClient = mongodb.MongoClient; | ||
9 | - | ||
10 | -const url = 'mongodb+srv://hellowhales:qogudtjr`12@cluster0.7gz7l.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'; | ||
11 | - | ||
12 | -let db; | ||
13 | - | ||
14 | -app.use(express.urlencoded({ extended: true })) | ||
15 | - | ||
16 | -// 터미널창 연결방법 mongo 'mongodb+srv://hellowhales:qogudtjr`12@cluster0.7gz7l.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'; | ||
17 | -// db.festivals.find({"title":"가무악극으로 만나는 토요 상설공연"}) 검색방법 | ||
18 | - | ||
19 | -//https://www.mongodb.com/try/download/database-tools?tck=docs_databasetools 여기서 mongoexport 실행을 위한 tool 다운받아서 Program files/mongodb/bin 폴더 안에 넣어줘야 함 | ||
20 | - | ||
21 | -MongoClient.connect(url, (error, client) => { // 서버열때 url 사용 mongoDB와 연결시키기 | ||
22 | - if (error) return console.log(error); | ||
23 | - db = client.db('myFirstDatabase'); // 클러스터의 데이터베이스를 db변수에 저장 | ||
24 | - app.listen(3001, () => { | ||
25 | - console.log('3001 port on'); | ||
26 | - }); | ||
27 | -}); | ||
28 | - | ||
29 | -app.get('/festivalList', (req, res) => { // localhost:3000/festivalList 입력하면 list.ejs에 저장한 형식대로 정보 불러와짐 | ||
30 | - //디비에 저장된 festivals 라는 collection안의 데이터(제목 또는 내용 등)를 꺼내기 | ||
31 | - db.collection('festivals').find().toArray((err, rslt) => { //DB에서 데이터를 찾음 festivals라는 collection안의 데이터를 꺼내게 됨 | ||
32 | - if (err) throw err; | ||
33 | - console.log(rslt); | ||
34 | - res.render('list.ejs', { posts: rslt }); // 찾은 데이터를 ejs 파일에 넣음 | ||
35 | - }); | ||
36 | -}); | ||
37 | - | ||
38 | - | ||
39 | - | ||
40 | -// 서버 연결하고 mongoexport 설치 후 mongoexport -d myFirstDatabase -c festivals -o festivalList.json /pretty 'mongodb+srv://hellowhales:qogudtjr`12@cluster0.7gz7l.mongodb.net/myFirstDatabase?retryWrites=true&w=majority' 명령 | ||
41 | -// 이용해서 myFirstDatabase라는 Db 안에서 festivals라는 collection 안의 데이터를 festivalList.json 파일로 export 해옴. | ||
42 | - | ||
43 | - | ||
44 | -var indexRouter = require('./routes/index'); | ||
45 | -var usersRouter = require('./routes/users'); | ||
46 | -const { mongo } = require('mongoose'); | ||
47 | - | ||
48 | - | ||
49 | - | ||
50 | -// view engine setup | ||
51 | - | ||
52 | -app.set('views', path.join(__dirname, 'views')); | ||
53 | -app.set('view engine', 'ejs'); // express에서 view엔진을 ejs로 설정하는과정 | ||
54 | - | ||
55 | - | ||
56 | - | ||
57 | -app.use(logger('dev')); | ||
58 | -app.use(express.json()); | ||
59 | -app.use(express.urlencoded({ extended: false })); | ||
60 | -app.use(cookieParser()); | ||
61 | -app.use(express.static(path.join(__dirname, 'public'))); | ||
62 | - | ||
63 | -app.use('/', indexRouter); | ||
64 | -app.use('/users', usersRouter); | ||
65 | - | ||
66 | -// catch 404 and forward to error handler | ||
67 | -app.use(function (req, res, next) { | ||
68 | - next(createError(404)); | ||
69 | -}); | ||
70 | - | ||
71 | -// error handler | ||
72 | -app.use(function (err, req, res, next) { | ||
73 | - // set locals, only providing error in development | ||
74 | - res.locals.message = err.message; | ||
75 | - res.locals.error = req.app.get('env') === 'development' ? err : {}; | ||
76 | - | ||
77 | - // render the error page | ||
78 | - res.status(err.status || 500); | ||
79 | - res.render('error'); | ||
80 | -}); | ||
81 | - | ||
82 | - | ||
83 | - | ||
84 | - | ||
85 | -module.exports = app; | ||
86 | - | ||
87 | -//유저 로그인할때 모든 여행지에 대한 정보를 DB에서 싹다 불러옴 페이지 렌더링할때 data 뿌려줌 | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
bin/www
deleted
100644 → 0
1 | -#!/usr/bin/env node | ||
2 | - | ||
3 | -/** | ||
4 | - * Module dependencies. | ||
5 | - */ | ||
6 | - | ||
7 | -var app = require('../app'); | ||
8 | -var debug = require('debug')('tft:server'); | ||
9 | -var http = require('http'); | ||
10 | - | ||
11 | -/** | ||
12 | - * Get port from environment and store in Express. | ||
13 | - */ | ||
14 | - | ||
15 | -var port = normalizePort(process.env.PORT || '3000'); | ||
16 | -app.set('port', port); | ||
17 | - | ||
18 | -/** | ||
19 | - * Create HTTP server. | ||
20 | - */ | ||
21 | - | ||
22 | -var server = http.createServer(app); | ||
23 | - | ||
24 | -/** | ||
25 | - * Listen on provided port, on all network interfaces. | ||
26 | - */ | ||
27 | - | ||
28 | -server.listen(port); | ||
29 | -server.on('error', onError); | ||
30 | -server.on('listening', onListening); | ||
31 | - | ||
32 | -/** | ||
33 | - * Normalize a port into a number, string, or false. | ||
34 | - */ | ||
35 | - | ||
36 | -function normalizePort(val) { | ||
37 | - var port = parseInt(val, 10); | ||
38 | - | ||
39 | - if (isNaN(port)) { | ||
40 | - // named pipe | ||
41 | - return val; | ||
42 | - } | ||
43 | - | ||
44 | - if (port >= 0) { | ||
45 | - // port number | ||
46 | - return port; | ||
47 | - } | ||
48 | - | ||
49 | - return false; | ||
50 | -} | ||
51 | - | ||
52 | -/** | ||
53 | - * Event listener for HTTP server "error" event. | ||
54 | - */ | ||
55 | - | ||
56 | -function onError(error) { | ||
57 | - if (error.syscall !== 'listen') { | ||
58 | - throw error; | ||
59 | - } | ||
60 | - | ||
61 | - var bind = typeof port === 'string' | ||
62 | - ? 'Pipe ' + port | ||
63 | - : 'Port ' + port; | ||
64 | - | ||
65 | - // handle specific listen errors with friendly messages | ||
66 | - switch (error.code) { | ||
67 | - case 'EACCES': | ||
68 | - console.error(bind + ' requires elevated privileges'); | ||
69 | - process.exit(1); | ||
70 | - break; | ||
71 | - case 'EADDRINUSE': | ||
72 | - console.error(bind + ' is already in use'); | ||
73 | - process.exit(1); | ||
74 | - break; | ||
75 | - default: | ||
76 | - throw error; | ||
77 | - } | ||
78 | -} | ||
79 | - | ||
80 | -/** | ||
81 | - * Event listener for HTTP server "listening" event. | ||
82 | - */ | ||
83 | - | ||
84 | -function onListening() { | ||
85 | - var addr = server.address(); | ||
86 | - var bind = typeof addr === 'string' | ||
87 | - ? 'pipe ' + addr | ||
88 | - : 'port ' + addr.port; | ||
89 | - debug('Listening on ' + bind); | ||
90 | -} |
gitignore
deleted
100644 → 0
package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
1 | { | 1 | { |
2 | - "name": "tft", | 2 | + "name": "svelte-base", |
3 | - "version": "0.0.0", | 3 | + "version": "1.0.0", |
4 | - "private": true, | 4 | + "description": "Basic svelte and express template I use for my application", |
5 | + "main": "server.js", | ||
5 | "scripts": { | 6 | "scripts": { |
6 | - "start": "node ./bin/www" | 7 | + "build": "webpack", |
8 | + "release": "cross-env NODE_ENV=production webpack", | ||
9 | + "server": "node server.js", | ||
10 | + "start": "npm run build & npm run server", | ||
11 | + "dev": "nodemon --watch ./src -e svelte,js,json --exec npm start" | ||
7 | }, | 12 | }, |
13 | + "repository": { | ||
14 | + "type": "git", | ||
15 | + "url": "git+https://github.com/Bigaston/svelte-base.git" | ||
16 | + }, | ||
17 | + "author": "Bigaston", | ||
18 | + "license": "UNLICENSED", | ||
19 | + "bugs": { | ||
20 | + "url": "https://github.com/Bigaston/svelte-base/issues" | ||
21 | + }, | ||
22 | + "homepage": "https://github.com/Bigaston/svelte-base#readme", | ||
8 | "dependencies": { | 23 | "dependencies": { |
9 | - "cookie-parser": "~1.4.4", | 24 | + "express": "^4.17.1", |
10 | - "debug": "~2.6.9", | 25 | + "jquery": "^3.6.0", |
11 | - "ejs": "~2.6.1", | 26 | + "nodemon": "^2.0.15", |
12 | - "express": "~4.16.1", | 27 | + "socket.io": "^4.3.2", |
13 | - "http-errors": "~1.6.3", | 28 | + "socket.io-client": "^4.3.2" |
14 | - "morgan": "~1.9.1" | 29 | + }, |
30 | + "devDependencies": { | ||
31 | + "cross-env": "^7.0.2", | ||
32 | + "css-loader": "^3.5.2", | ||
33 | + "style-loader": "^1.1.3", | ||
34 | + "svelte": "^3.20.1", | ||
35 | + "svelte-loader": "^2.13.6", | ||
36 | + "webpack": "^4.42.1", | ||
37 | + "webpack-cli": "^3.3.11" | ||
15 | } | 38 | } |
16 | } | 39 | } | ... | ... |
public/TFTlogo.png
0 → 100644
6.12 KB
public/build/bundle.js
0 → 100644
This diff could not be displayed because it is too large.
public/build/bundle.js.map
0 → 100644
This diff could not be displayed because it is too large.
public/exploremap.png
0 → 100644
540 KB
public/favicon.png
0 → 100644
3.05 KB
public/global.css
0 → 100644
1 | +html, body { | ||
2 | + position: relative; | ||
3 | + width: 100%; | ||
4 | + height: 100%; | ||
5 | +} | ||
6 | + | ||
7 | +body { | ||
8 | + color: #333; | ||
9 | + margin: 0; | ||
10 | + padding: 8px; | ||
11 | + box-sizing: border-box; | ||
12 | + font-family: '맑은 고딕 Semilight', "AppleSDGothicNeo-Regular", 'Malgun Gothic', '맑은 고딕', "dotum", '돋움', sans-serif; | ||
13 | +} | ||
14 | + | ||
15 | +a { | ||
16 | + color: rgb(0,100,200); | ||
17 | + text-decoration: none; | ||
18 | +} | ||
19 | + | ||
20 | +a:hover { | ||
21 | + text-decoration: underline; | ||
22 | +} | ||
23 | + | ||
24 | +a:visited { | ||
25 | + color: rgb(0,80,160); | ||
26 | +} | ||
27 | + | ||
28 | +label { | ||
29 | + display: block; | ||
30 | +} | ||
31 | + | ||
32 | +input, button, select, textarea { | ||
33 | + font-family: inherit; | ||
34 | + font-size: inherit; | ||
35 | + -webkit-padding: 0.4em 0; | ||
36 | + padding: 0.4em; | ||
37 | + margin: 0 0 0.5em 0; | ||
38 | + box-sizing: border-box; | ||
39 | + border: 1px solid #ccc; | ||
40 | + border-radius: 2px; | ||
41 | +} | ||
42 | + | ||
43 | +input:disabled { | ||
44 | + color: #ccc; | ||
45 | +} | ||
46 | + | ||
47 | +button { | ||
48 | + color: #333; | ||
49 | + background-color: #f4f4f4; | ||
50 | + outline: none; | ||
51 | +} | ||
52 | + | ||
53 | +button:disabled { | ||
54 | + color: #999; | ||
55 | +} | ||
56 | + | ||
57 | +button:not(:disabled):active { | ||
58 | + background-color: #ddd; | ||
59 | +} | ||
60 | + | ||
61 | +button:focus { | ||
62 | + border-color: #666; | ||
63 | +} |
public/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="ko"> | ||
3 | +<head> | ||
4 | + <meta charset='utf-8'> | ||
5 | + <meta name='viewport' content='width=device-width,initial-scale=1'> | ||
6 | + <link rel='icon' type='image/png' href='/public/favicon.png'> | ||
7 | + <link rel='stylesheet' href='/public/global.css'> | ||
8 | + | ||
9 | +</head> | ||
10 | + | ||
11 | +<body> | ||
12 | + <noscript> | ||
13 | + <p>This website need JavaScript to work</p> | ||
14 | + </noscript> | ||
15 | + | ||
16 | + <main></main> | ||
17 | + | ||
18 | + <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=046d27f1430524731d3ec6870f0c8923"></script> | ||
19 | + <script src='./public/build/bundle.js'></script> | ||
20 | +</body> | ||
21 | +</html> |
public/infofesta.png
0 → 100644
6.67 KB
public/map-pin.png
0 → 100644
18.3 KB
public/stacks.png
0 → 100644
46.6 KB
public/stylesheets/style.css
deleted
100644 → 0
routes/index.js
deleted
100644 → 0
routes/users.js
deleted
100644 → 0
server.js
0 → 100644
1 | +const express = require('express') | ||
2 | +const path = require("path") | ||
3 | + | ||
4 | +var app = express() | ||
5 | + | ||
6 | +const PORT = 1697; | ||
7 | + | ||
8 | +app.use("/public", express.static('./public')); | ||
9 | + | ||
10 | +app.get("/*", (req, res) => { | ||
11 | + res.sendFile(path.join(__dirname, "./public/index.html")) | ||
12 | +}) | ||
13 | + | ||
14 | +var serv = app.listen(PORT, () => console.log(`Serveur lauched on port ${PORT}`)) |
src/App.svelte
0 → 100644
1 | +<script> | ||
2 | + import Menu from './Menu.svelte'; | ||
3 | + import Backtotop from './SideItems/Backtotop.svelte'; | ||
4 | + import Map from './Map.svelte'; | ||
5 | + import Article from './Articles.svelte'; | ||
6 | + import { AllFestas } from './Stores/AllFestas'; | ||
7 | + import jQuery from 'jquery'; | ||
8 | + import { DisplayedFestas } from './Stores/DisplayedFestas'; | ||
9 | + | ||
10 | + function LoadFestas() { | ||
11 | + let url = "http://api.visitkorea.or.kr/openapi/service/rest/KorService/areaBasedList?ServiceKey=2lFkvQJYgzOOhwUKiUt8aZVNpd1PpBOf%2FfMNW17cl25DE0GUEDddeR9iGnuSUpggjUoIUgamfhcvnKQ3eH1dAw%3D%3D&contentTypeId=15&areaCode=&sigunguCode=&cat1=&cat2=&cat3=&listYN=Y&MobileOS=ETC&MobileApp=TourAPI3.0_Guide&arrange=A&numOfRows=12&pageNo=1&_type=json"; | ||
12 | + jQuery.getJSON(url, (json) => { | ||
13 | + AllFestas.set(json.response.body.items.item); | ||
14 | + }); | ||
15 | + } | ||
16 | +</script> | ||
17 | + | ||
18 | +<style> | ||
19 | + div { | ||
20 | + text-align: left; | ||
21 | + box-sizing: border-box; | ||
22 | + display: block; | ||
23 | + } | ||
24 | + | ||
25 | + :global(body) { | ||
26 | + padding: 0; | ||
27 | + margin: 0; | ||
28 | + align-items: center; | ||
29 | + align-content: center; | ||
30 | + text-align: center; | ||
31 | + justify-content: center; | ||
32 | + } | ||
33 | + | ||
34 | + :global(html) { | ||
35 | + scroll-behavior: smooth; | ||
36 | + } | ||
37 | + | ||
38 | +</style> | ||
39 | + | ||
40 | +<svelte:window on:load|once={LoadFestas}></svelte:window> | ||
41 | +<div> | ||
42 | + <Menu/> | ||
43 | + <Map/> | ||
44 | + <Backtotop/> | ||
45 | + <Article/> | ||
46 | +</div> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/Articles.svelte
0 → 100644
1 | +<style> | ||
2 | + .article{ | ||
3 | + font-family: 맑은고딕, Malgun Gothic, dotum, gulim, sans-serif; | ||
4 | + font-size: 24pt; | ||
5 | + padding:10vw; | ||
6 | + text-align: center; | ||
7 | + display: flex; | ||
8 | + } | ||
9 | + | ||
10 | + .wbg{ | ||
11 | + background-color: #ffffff; | ||
12 | + } | ||
13 | + | ||
14 | + .gbg{ | ||
15 | + background-color: #edeff2; | ||
16 | + } | ||
17 | + | ||
18 | + .article img { | ||
19 | + display: inline-block; | ||
20 | + max-width: 500px; | ||
21 | + width: 20vw; | ||
22 | + margin-left: 4vw; | ||
23 | + margin-right: 4vw; | ||
24 | + } | ||
25 | + | ||
26 | + .article .text { | ||
27 | + display: inline-block; | ||
28 | + width: 50vw; | ||
29 | + } | ||
30 | + | ||
31 | + .contents{ | ||
32 | + display: flex; | ||
33 | + align-items: center; | ||
34 | + } | ||
35 | +</style> | ||
36 | +<script> | ||
37 | +</script> | ||
38 | + | ||
39 | +<div class="article wbg" id="article1"> | ||
40 | + <div class="contents"> | ||
41 | + <img src="./public/TFTlogo.png" alt=":)"> | ||
42 | + <div class="text"> | ||
43 | + <h3>TFT에 오신 걸 환영합니다!</h3> | ||
44 | + Travel & Festival with Temperature<br> | ||
45 | + 각종 행사 정보와 날씨 정보를 한눈에 알 수 있는 서비스입니다. | ||
46 | + </div> | ||
47 | + </div> | ||
48 | +</div> | ||
49 | +<div class="article gbg" id="article2"> | ||
50 | + <div class="contents"> | ||
51 | + <div class="text"> | ||
52 | + <h3>손쉬운 행사 탐색</h3> | ||
53 | + TFT를 통해 전국에서 개최중인 행사와 축제를<br> | ||
54 | + 한 눈에 확인할 수 있습니다. | ||
55 | + </div> | ||
56 | + <img src="./public/exploremap.png" alt=";("> | ||
57 | + </div> | ||
58 | +</div> | ||
59 | +<div class="article wbg" id="article3"> | ||
60 | + <div class="contents"> | ||
61 | + <img src="./public/infofesta.png" alt=":)"> | ||
62 | + <div class="text"> | ||
63 | + <h3>행사 정보 확인</h3> | ||
64 | + 지도에 표시된 마커를 클릭하여<br> | ||
65 | + 행사 정보를 확인할 수 있습니다. | ||
66 | + </div> | ||
67 | + </div> | ||
68 | +</div> | ||
69 | +<div class="article gbg" id="article4"> | ||
70 | + <div class="contents"> | ||
71 | + <div class="text"> | ||
72 | + 사용된 기술 스택은 다음과 같습니다.<br><br> | ||
73 | + WEB : NodeJS / Express / Svelte<br> | ||
74 | + Database : MongoDB<br> | ||
75 | + API Handler : NodeJS / RequestJS<br> | ||
76 | + Docker | ||
77 | + </div> | ||
78 | + <img src="./public/stacks.png" alt=";("> | ||
79 | + </div> | ||
80 | +</div> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/Data/coordinates.js
0 → 100644
This diff is collapsed. Click to expand it.
src/Data/district.js
0 → 100644
1 | +export let DISTRICTS = { | ||
2 | + "name": "korea-administrative-district", | ||
3 | + "version": "20160125", | ||
4 | + "url": "https://github.com/cosmosfarm/korea-administrative-district", | ||
5 | + "data":[ | ||
6 | + {"미선택":[ | ||
7 | + "미선택" | ||
8 | + ]}, | ||
9 | + {"서울특별시":[ | ||
10 | + "미선택", "종로구", "중구", "용산구", "성동구", "광진구", "동대문구", "중랑구", "성북구", "강북구", "도봉구", "노원구", "은평구", "서대문구", "마포구", "양천구", "강서구", "구로구", "금천구", "영등포구", "동작구", "관악구", "서초구", "강남구", "송파구", "강동구" | ||
11 | + ]}, | ||
12 | + {"부산광역시":[ | ||
13 | + "미선택", "중구", "서구", "동구", "영도구", "부산진구", "동래구", "남구", "북구", "강서구", "해운대구", "사하구", "금정구", "연제구", "수영구", "사상구", "기장군" | ||
14 | + ]}, | ||
15 | + {"인천광역시":[ | ||
16 | + "미선택", "중구", "동구", "남구", "연수구", "남동구", "부평구", "계양구", "서구", "강화군", "옹진군" | ||
17 | + ]}, | ||
18 | + {"대구광역시":[ | ||
19 | + "미선택", "중구", "동구", "서구", "남구", "북구", "수성구", "달서구", "달성군" | ||
20 | + ]}, | ||
21 | + {"광주광역시":[ | ||
22 | + "미선택", "동구", "서구", "남구", "북구", "광산구" | ||
23 | + ]}, | ||
24 | + {"대전광역시":[ | ||
25 | + "미선택", "동구", "중구", "서구", "유성구", "대덕구" | ||
26 | + ]}, | ||
27 | + {"울산광역시":[ | ||
28 | + "미선택", "중구", "남구", "동구", "북구", "울주군" | ||
29 | + ]}, | ||
30 | + {"세종특별자치시":[ | ||
31 | + "미선택", "세종특별자치시" | ||
32 | + ]}, | ||
33 | + {"경기도":[ | ||
34 | + "미선택", "가평군", "고양시", "과천시", "광명시", "광주시", "구리시", "군포시", "김포시", "남양주시", "동두천시", "부천시", "성남시", "수원시", "시흥시", "안산시", "안성시", "안양시", "양주시", "양평군", "여주시", "연천군", "오산시", "용인시", "의왕시", "의정부시", "이천시", "파주시", "평택시", "포천시", "하남시", "화성시" | ||
35 | + ]}, | ||
36 | + {"강원도":[ | ||
37 | + "미선택", "원주시", "춘천시", "강릉시", "동해시", "속초시", "삼척시", "홍천군", "태백시", "철원군", "횡성군", "평창군", "영월군", "정선군", "인제군", "고성군", "양양군", "화천군", "양구군" | ||
38 | + ]}, | ||
39 | + {"충청북도":[ | ||
40 | + "미선택", "청주시", "충주시", "제천시", "보은군", "옥천군", "영동군", "증평군", "진천군", "괴산군", "음성군", "단양군" | ||
41 | + ]}, | ||
42 | + {"충청남도":[ | ||
43 | + "미선택", "천안시", "공주시", "보령시", "아산시", "서산시", "논산시", "계룡시", "당진시", "금산군", "부여군", "서천군", "청양군", "홍성군", "예산군", "태안군" | ||
44 | + ]}, | ||
45 | + {"경상북도":[ | ||
46 | + "미선택", "포항시", "경주시", "김천시", "안동시", "구미시", "영주시", "영천시", "상주시", "문경시", "경산시", "군위군", "의성군", "청송군", "영양군", "영덕군", "청도군", "고령군", "성주군", "칠곡군", "예천군", "봉화군", "울진군", "울릉군" | ||
47 | + ]}, | ||
48 | + {"경상남도":[ | ||
49 | + "미선택", "창원시", "김해시", "진주시", "양산시", "거제시", "통영시", "사천시", "밀양시", "함안군", "거창군", "창녕군", "고성군", "하동군", "합천군", "남해군", "함양군", "산청군", "의령군" | ||
50 | + ]}, | ||
51 | + {"전라북도":[ | ||
52 | + "미선택", "전주시", "익산시", "군산시", "정읍시", "완주군", "김제시", "남원시", "고창군", "부안군", "임실군", "순창군", "진안군", "장수군", "무주군" | ||
53 | + ]}, | ||
54 | + {"전라남도":[ | ||
55 | + "미선택", "여수시", "순천시", "목포시", "광양시", "나주시", "무안군", "해남군", "고흥군", "화순군", "영암군", "영광군", "완도군", "담양군", "장성군", "보성군", "신안군", "장흥군", "강진군", "함평군", "진도군", "곡성군", "구례군" | ||
56 | + ]}, | ||
57 | + {"제주특별자치도":[ | ||
58 | + "미선택", "제주시", "서귀포시" | ||
59 | + ]} | ||
60 | + ] | ||
61 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/Map.svelte
0 → 100644
1 | +<style> | ||
2 | + #map_bg { | ||
3 | + box-sizing: border-box; | ||
4 | + justify-content: center; | ||
5 | + display: flex; | ||
6 | + height: 100vh; | ||
7 | + background-color:#edeff2; | ||
8 | + } | ||
9 | + | ||
10 | + #map { | ||
11 | + margin-top: 50px; | ||
12 | + box-sizing: border-box; | ||
13 | + display: flex; | ||
14 | + height: 80vh; | ||
15 | + width: 60vw; | ||
16 | + background-color:#edeff2; | ||
17 | + border-radius: 10px; | ||
18 | + border: 1px solid black; | ||
19 | + box-shadow: 0 10px 20px rgb(0 0 0 / 15%); | ||
20 | + } | ||
21 | +</style> | ||
22 | + | ||
23 | +<script> | ||
24 | + import Info from './SideItems/Info.svelte'; | ||
25 | + import Festalist from './SideItems/Festalist.svelte'; | ||
26 | + import Arealist from './SideItems/Arealist.svelte'; | ||
27 | + import { COORDINATES } from "./Data/coordinates"; | ||
28 | + import { DisplayedFestas } from "./Stores/DisplayedFestas"; | ||
29 | + | ||
30 | + var {kakao} = window; | ||
31 | + | ||
32 | + var ShowArea = false; | ||
33 | + var ShowFesta = false; | ||
34 | + var ShowInfo = false; | ||
35 | + | ||
36 | + var Festa = {}; | ||
37 | + var markers = []; | ||
38 | + var infowindows = []; | ||
39 | + var map; | ||
40 | + | ||
41 | + function createMap() { | ||
42 | + var mapContainer = document.getElementById('map'), // 지도를 표시할 div | ||
43 | + mapOption = { | ||
44 | + center: new kakao.maps.LatLng(36.46682, 127.37865), // 지도의 중심좌표 | ||
45 | + level: 12, // 지도의 확대 레벨 | ||
46 | + mapTypeId : kakao.maps.MapTypeId.ROADMAP // 지도종류 | ||
47 | + }; | ||
48 | + | ||
49 | + markers= []; | ||
50 | + map = new kakao.maps.Map(mapContainer, mapOption); // 카카오 지도 | ||
51 | + } | ||
52 | + | ||
53 | + $: drawMarkers($DisplayedFestas); | ||
54 | + | ||
55 | + function drawMarkers (data) { | ||
56 | + let len = data.length >= 9 ? 9 : data.length; | ||
57 | + | ||
58 | + markers.forEach( mk => { | ||
59 | + mk.setMap(null); | ||
60 | + }); | ||
61 | + infowindows.forEach( iw => { | ||
62 | + iw.close(); | ||
63 | + }); | ||
64 | + markers, infowindows = [], []; | ||
65 | + | ||
66 | + for(var i = 0; i < len; i++){ | ||
67 | + // 지도에 마커를 생성하고 표시한다 | ||
68 | + let marker = new kakao.maps.Marker({ | ||
69 | + position: new kakao.maps.LatLng(data[i].mapy, data[i].mapx), // 마커의 좌표 | ||
70 | + map: map // 마커를 표시할 지도 객체 | ||
71 | + }); | ||
72 | + | ||
73 | + let infowindow = new kakao.maps.InfoWindow({ | ||
74 | + content : '<div style="padding:5px;white-space:nowrap;">' + data[i].title + '</div>' // 인포윈도우에 표시할 내용 | ||
75 | + }); | ||
76 | + infowindow.open(map, marker); | ||
77 | + | ||
78 | + // 마커 클릭 시 이벤트 | ||
79 | + let showInfo = function(festa) { | ||
80 | + return function() { | ||
81 | + Festa = festa; | ||
82 | + ShowInfo = true; | ||
83 | + console.log(Festa); | ||
84 | + } | ||
85 | + }; | ||
86 | + kakao.maps.event.addListener(marker, 'click', showInfo(data[i])); | ||
87 | + infowindows.push(infowindow); | ||
88 | + markers.push(marker); | ||
89 | + | ||
90 | + }; | ||
91 | + } | ||
92 | + | ||
93 | + function moveTo(district, city) { | ||
94 | + let [x, y, level] = COORDINATES[district][city]; | ||
95 | + var moveLatLon = new kakao.maps.LatLng(y, x); | ||
96 | + map.setLevel(level); | ||
97 | + map.panTo(moveLatLon); | ||
98 | + } | ||
99 | + | ||
100 | + function closeBars() { | ||
101 | + ShowArea = false; | ||
102 | + ShowFesta = false; | ||
103 | + ShowInfo = false; | ||
104 | + } | ||
105 | + | ||
106 | +</script> | ||
107 | + | ||
108 | +<svelte:window on:load={createMap}></svelte:window> | ||
109 | +<Festalist bind:sidebar_show={ShowFesta}/> | ||
110 | +<Arealist bind:sidebar_show={ShowArea} {moveTo}/> | ||
111 | +<Info bind:sidebar_show={ShowInfo} bind:festa={Festa}/> | ||
112 | +<div id="map_bg"> | ||
113 | + <div id="map"></div> | ||
114 | +</div> |
src/Menu.svelte
0 → 100644
1 | +<script> | ||
2 | + import Menuitems from './Menuitems.svelte' | ||
3 | + | ||
4 | + // 메뉴 바 항목들 | ||
5 | + const navItems = [ | ||
6 | + { label: "TFT 소개", href: "#article1" }, | ||
7 | + { label: "행사 탐색", href: "#article2" }, | ||
8 | + { label: "행사 정보", href: "#article3" }, | ||
9 | + { label: "기술 스택", href: "#article4" } | ||
10 | + ]; | ||
11 | +</script> | ||
12 | + | ||
13 | +<style> | ||
14 | + nav { | ||
15 | + background-color: #edeff2; | ||
16 | + font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; | ||
17 | + height: 70px; | ||
18 | + /* width: 50vw; */ | ||
19 | + top: 0; | ||
20 | + } | ||
21 | + | ||
22 | + .inner { | ||
23 | + max-width: 700px; | ||
24 | + padding-left: 10px; | ||
25 | + padding-right: 10px; | ||
26 | + margin: auto; | ||
27 | + box-sizing: border-box; | ||
28 | + display: flex; | ||
29 | + align-items: center; | ||
30 | + justify-content: center; | ||
31 | + height: 100%; | ||
32 | + } | ||
33 | + | ||
34 | + .logo img{ | ||
35 | + display: flex; | ||
36 | + width: 120px; | ||
37 | + left: 20px; | ||
38 | + top: 15px; | ||
39 | + margin-right: 400px; | ||
40 | + } | ||
41 | + | ||
42 | + .navbar-list { | ||
43 | + display: flex; | ||
44 | + padding: 0; | ||
45 | + width: 100%; | ||
46 | + justify-content: space-between; | ||
47 | + margin: 0; | ||
48 | + } | ||
49 | +</style> | ||
50 | + | ||
51 | +<nav id="nav"> | ||
52 | + <div class="inner"> | ||
53 | + <div class="logo"><img alt="Logo" src="./public/TFTlogo.png"></div> | ||
54 | + <ul class='navbar-list'> | ||
55 | + {#each navItems as item} | ||
56 | + <Menuitems {item}/> | ||
57 | + {/each} | ||
58 | + </ul> | ||
59 | + </div> | ||
60 | +</nav> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/Menuitems.svelte
0 → 100644
1 | +<style> | ||
2 | + li { | ||
3 | + display: flex; | ||
4 | + list-style-type: none; | ||
5 | + position: relative; | ||
6 | + margin-left: 50px; | ||
7 | + height: 45px; | ||
8 | + white-space: nowrap; | ||
9 | + align-items: center; | ||
10 | + } | ||
11 | + | ||
12 | + li:before { | ||
13 | + content: ""; | ||
14 | + position: absolute; | ||
15 | + bottom: 0; | ||
16 | + left: 0; | ||
17 | + width: 100%; | ||
18 | + height: 1px; | ||
19 | + } | ||
20 | + | ||
21 | + a { | ||
22 | + color: #000000; | ||
23 | + text-decoration: none; | ||
24 | + display: inline-flex; | ||
25 | + padding: 0 10px; | ||
26 | + font-size: 20px; | ||
27 | + } | ||
28 | + | ||
29 | + @media only screen and (max-width: 767px) { | ||
30 | + a { | ||
31 | + display: none; | ||
32 | + } | ||
33 | + } | ||
34 | + | ||
35 | + .onmouse { | ||
36 | + text-decoration: underline; | ||
37 | + } | ||
38 | +</style> | ||
39 | + | ||
40 | +<script> | ||
41 | + export let item; | ||
42 | + let onmouse; | ||
43 | + | ||
44 | + function enter() { | ||
45 | + onmouse = true; | ||
46 | + } | ||
47 | + | ||
48 | + function leave() { | ||
49 | + onmouse = false; | ||
50 | + } | ||
51 | +</script> | ||
52 | + | ||
53 | +<li on:mouseenter={enter} on:mouseleave={leave} > | ||
54 | + <a href={item.href} class:onmouse>{item.label}</a> | ||
55 | +</li> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/SideItems/Arealist.svelte
0 → 100644
1 | +<script> | ||
2 | + import { fly } from 'svelte/transition' | ||
3 | + import SideBar from './SideBar.svelte'; | ||
4 | + var icon_show = true; | ||
5 | + var sidebar_show = false; | ||
6 | + | ||
7 | + function showBar() { | ||
8 | + sidebar_show = true; | ||
9 | + } | ||
10 | + | ||
11 | + function hide() { | ||
12 | + if (window.scrollY < 400) { | ||
13 | + icon_show = true; | ||
14 | + } else { | ||
15 | + icon_show = false; | ||
16 | + sidebar_show = false; | ||
17 | + } | ||
18 | + } | ||
19 | + | ||
20 | + import { DISTRICTS } from '../Data/district' | ||
21 | + import { District, City, Changed } from '../Stores/DistrictStore' | ||
22 | + | ||
23 | + export var moveTo; | ||
24 | + | ||
25 | + let curDisrict = {"미선택" : ["미선택"]}; | ||
26 | + let curCity = "미선택"; | ||
27 | + | ||
28 | + $: if(Object.keys(curDisrict)[0] != "미선택") { | ||
29 | + District.set(Object.keys(curDisrict)[0]); | ||
30 | + } else { | ||
31 | + District.set(""); | ||
32 | + }; | ||
33 | + | ||
34 | + $: if(curCity != "미선택") { | ||
35 | + City.set(curCity); | ||
36 | + } else { | ||
37 | + City.set(""); | ||
38 | + } | ||
39 | + | ||
40 | + function setDistrict(district) { | ||
41 | + curDisrict = district; | ||
42 | + if(Object.keys(curDisrict)[0] == "세종특별자치시") | ||
43 | + curCity = "세종특별자치시"; | ||
44 | + else | ||
45 | + curCity = "미선택"; | ||
46 | + moveTo(Object.keys(curDisrict)[0], curCity); | ||
47 | + Changed.set(true); | ||
48 | + } | ||
49 | + | ||
50 | + function setCity(city) { | ||
51 | + curCity = city; | ||
52 | + moveTo(Object.keys(curDisrict)[0], curCity); | ||
53 | + Changed.set(true); | ||
54 | + } | ||
55 | + | ||
56 | +</script> | ||
57 | + | ||
58 | +<style> | ||
59 | + .sidebtn { | ||
60 | + border-radius: 5px; | ||
61 | + border: 0px; | ||
62 | + width: 70px; | ||
63 | + height: 70px; | ||
64 | + position: fixed; | ||
65 | + left: -20px; | ||
66 | + top: 120px; | ||
67 | + padding-right: 10px; | ||
68 | + text-align: right; | ||
69 | + background-color: #b71c1c; | ||
70 | + color: #ffffff; | ||
71 | + box-shadow: 0 10px 20px rgb(0 0 0 / 15%); | ||
72 | + } | ||
73 | + | ||
74 | + .sidebtn:focus { | ||
75 | + background-color: #ef5350; | ||
76 | + } | ||
77 | + | ||
78 | + .sidebtn:hover { | ||
79 | + background-color: #d32f2f; | ||
80 | + } | ||
81 | + | ||
82 | + .areas { | ||
83 | + display: flex; | ||
84 | + border-bottom: #999999 solid 1px; | ||
85 | + width: 100%; | ||
86 | + flex-wrap: wrap; | ||
87 | + justify-content: space-evenly; | ||
88 | + } | ||
89 | + | ||
90 | + .areatitle { | ||
91 | + display: inline-block; | ||
92 | + text-align:center; | ||
93 | + width: 200px; | ||
94 | + } | ||
95 | + | ||
96 | + .district { | ||
97 | + display: inline-block; | ||
98 | + text-align:center; | ||
99 | + width: 125px; | ||
100 | + margin: 5px; | ||
101 | + border: 1px solid black; | ||
102 | + border-radius: 5px; | ||
103 | + padding-top: 5px; | ||
104 | + padding-bottom: 5px; | ||
105 | + } | ||
106 | + | ||
107 | + .district:hover { | ||
108 | + background-color: #ffebee; | ||
109 | + } | ||
110 | + | ||
111 | + .district:focus { | ||
112 | + background-color: #ffcdd2; | ||
113 | + } | ||
114 | + | ||
115 | + .district.selected{ | ||
116 | + background-color: #ffcdd2; | ||
117 | + } | ||
118 | + | ||
119 | + .city { | ||
120 | + display: inline-block; | ||
121 | + text-align:center; | ||
122 | + width: 125px; | ||
123 | + margin: 5px; | ||
124 | + border: 1px solid black; | ||
125 | + border-radius: 5px; | ||
126 | + padding-top: 5px; | ||
127 | + padding-bottom: 5px; | ||
128 | + } | ||
129 | + | ||
130 | + .city:hover { | ||
131 | + background-color: #ffebee; | ||
132 | + } | ||
133 | + | ||
134 | + .city:focus { | ||
135 | + background-color: #ffcdd2; | ||
136 | + } | ||
137 | + | ||
138 | + .city.selected{ | ||
139 | + background-color: #ffcdd2; | ||
140 | + } | ||
141 | + | ||
142 | +</style> | ||
143 | + | ||
144 | +<svelte:window on:scroll={hide}></svelte:window> | ||
145 | + | ||
146 | +{#if (icon_show)} | ||
147 | + <button class="sidebtn" | ||
148 | + transition:fly="{{ x : -50, duration : 400}}" | ||
149 | + on:click={showBar}> | ||
150 | + 지역<br>선택 | ||
151 | + </button> | ||
152 | +{/if} | ||
153 | +<SideBar bind:show={sidebar_show}> | ||
154 | + <div class="areas"> | ||
155 | + <div class="areatitle">광역시/도</div> | ||
156 | + {#each DISTRICTS.data as d} | ||
157 | + <div class="{Object.keys(curDisrict)[0] == Object.keys(d)[0] ? 'district selected' : 'district'}" | ||
158 | + on:click={() => {setDistrict(d)}}> | ||
159 | + {Object.keys(d)[0]} | ||
160 | + </div><br> | ||
161 | + {/each} | ||
162 | + </div><br> | ||
163 | + <div class="areas"> | ||
164 | + <div class="areatitle">시/군/구</div> | ||
165 | + {#each curDisrict[Object.keys(curDisrict)[0]] as c} | ||
166 | + <div class="{curCity == c ? 'city selected' : 'city'}" | ||
167 | + on:click={() => {setCity(c)}}> | ||
168 | + {c} | ||
169 | + </div><br> | ||
170 | + {/each} | ||
171 | + </div> | ||
172 | +</SideBar> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/SideItems/Backtotop.svelte
0 → 100644
1 | +<script> | ||
2 | + import { fly } from 'svelte/transition' | ||
3 | + let show = false; | ||
4 | + | ||
5 | + function scrollUp() { | ||
6 | + document.body.scrollIntoView({behavior: "smooth"}) | ||
7 | + } | ||
8 | + | ||
9 | + function hide() { | ||
10 | + if (window.scrollY > 60) { | ||
11 | + show = true | ||
12 | + } else { | ||
13 | + show = false | ||
14 | + } | ||
15 | + } | ||
16 | +</script> | ||
17 | + | ||
18 | +<style> | ||
19 | + #backtotop { | ||
20 | + border-radius: 100%; | ||
21 | + width: 50px; | ||
22 | + height: 50px; | ||
23 | + position: fixed; | ||
24 | + right: 4%; | ||
25 | + bottom: 10%; | ||
26 | + } | ||
27 | + | ||
28 | +</style> | ||
29 | + | ||
30 | +<svelte:window on:scroll={hide}></svelte:window> | ||
31 | + | ||
32 | +{#if (show)} | ||
33 | + <button id="backtotop" | ||
34 | + transition:fly="{{ y : 200, duration:400}}" | ||
35 | + on:click={scrollUp}> | ||
36 | + ▲ | ||
37 | + </button> | ||
38 | +{/if} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/SideItems/Festalist.svelte
0 → 100644
1 | +<script> | ||
2 | + import { fly } from 'svelte/transition' | ||
3 | + import SideBar from './SideBar.svelte'; | ||
4 | + let icon_show = true; | ||
5 | + let sidebar_show = false; | ||
6 | + | ||
7 | + function showBar() { | ||
8 | + sidebar_show = true; | ||
9 | + } | ||
10 | + | ||
11 | + function hide() { | ||
12 | + if (window.scrollY < 400) { | ||
13 | + icon_show = true; | ||
14 | + } else { | ||
15 | + icon_show = false; | ||
16 | + sidebar_show = false; | ||
17 | + } | ||
18 | + } | ||
19 | + | ||
20 | + import { District, City, Changed } from '../Stores/DistrictStore'; | ||
21 | + import { AllFestas } from "../Stores/AllFestas"; | ||
22 | + import { DisplayedFestas } from "../Stores/DisplayedFestas"; | ||
23 | + | ||
24 | + let festaChecked = []; | ||
25 | + $: festaList = $AllFestas.filter( v => { | ||
26 | + if(v.addr1) { | ||
27 | + let district = v.addr1.split(" ")[0]; | ||
28 | + let city = v.addr1.split(" ")[1]; | ||
29 | + return ($District === "" || district === $District) && | ||
30 | + ($City === "" || city === $City); | ||
31 | + } else { | ||
32 | + return false; | ||
33 | + } | ||
34 | + }); | ||
35 | + $: festaParsed = festaList.map( (v, i) => { | ||
36 | + return { "id" : i, "title" : v.title, "addr1" : v.addr1, "contentid" : v.contentid, "checked" : false } | ||
37 | + }); | ||
38 | + $: if ($Changed) { | ||
39 | + let len = festaList.length >= 9 ? 9 : festaList.length; | ||
40 | + festaChecked = []; | ||
41 | + for(let i = 0; i < len; i++) | ||
42 | + check(i); | ||
43 | + } | ||
44 | + $: DisplayedFestas.set(festaChecked); | ||
45 | + | ||
46 | + function check(idx) { | ||
47 | + if (!festaParsed[idx].checked) { | ||
48 | + if(festaChecked.length <= 8) { | ||
49 | + festaParsed[idx].checked = true; | ||
50 | + festaChecked = festaChecked.concat(festaList[idx]); | ||
51 | + } else { | ||
52 | + alert("9개 이상 선택하실 수 없습니다."); | ||
53 | + } | ||
54 | + } else { | ||
55 | + festaParsed[idx].checked = false; | ||
56 | + festaChecked = festaChecked.filter( v => { | ||
57 | + return v.contentid !== festaParsed[idx].contentid; | ||
58 | + }); | ||
59 | + } | ||
60 | + } | ||
61 | +</script> | ||
62 | + | ||
63 | +<style> | ||
64 | + .sidebtn { | ||
65 | + border-radius: 5px; | ||
66 | + border: 0px; | ||
67 | + width: 70px; | ||
68 | + height: 70px; | ||
69 | + position: fixed; | ||
70 | + left: -20px; | ||
71 | + top: 200px; | ||
72 | + padding-right: 10px; | ||
73 | + text-align: right; | ||
74 | + background-color: #e65100; | ||
75 | + color: #ffffff; | ||
76 | + box-shadow: 0 10px 20px rgb(0 0 0 / 15%); | ||
77 | + } | ||
78 | + | ||
79 | + .sidebtn:focus { | ||
80 | + background-color: #f57c00; | ||
81 | + } | ||
82 | + | ||
83 | + .sidebtn:hover { | ||
84 | + background-color: #ef6c00; | ||
85 | + } | ||
86 | + | ||
87 | + .festa { | ||
88 | + border: 1px solid #aaa; | ||
89 | + border-radius: 2px; | ||
90 | + box-shadow: 2px 2px 8px rgba(0,0,0,0.1); | ||
91 | + padding: 2px; | ||
92 | + margin-bottom: 5px; | ||
93 | + } | ||
94 | + | ||
95 | + .festa:hover { | ||
96 | + background-color: #fff3e0; | ||
97 | + } | ||
98 | + | ||
99 | + .festa:focus { | ||
100 | + background-color: #ffe0b2; | ||
101 | + } | ||
102 | + | ||
103 | + .selected { | ||
104 | + background-color: #ffe0b2; | ||
105 | + border: 1px solid #aaa; | ||
106 | + border-radius: 2px; | ||
107 | + box-shadow: 2px 2px 8px rgba(0,0,0,0.1); | ||
108 | + padding: 2px; | ||
109 | + margin-bottom: 5px; | ||
110 | + } | ||
111 | + | ||
112 | + .title { | ||
113 | + font-weight: bold; | ||
114 | + border-bottom: 2px solid #ff3e00; | ||
115 | + } | ||
116 | + | ||
117 | + .addr { | ||
118 | + display: flex; | ||
119 | + align-items: center; | ||
120 | + } | ||
121 | + | ||
122 | + .addr img { | ||
123 | + width: 20px; | ||
124 | + height: 20px; | ||
125 | + } | ||
126 | + | ||
127 | +</style> | ||
128 | + | ||
129 | +<svelte:window on:scroll={hide}></svelte:window> | ||
130 | + | ||
131 | +{#if (icon_show)} | ||
132 | + <button class="sidebtn" | ||
133 | + transition:fly="{{ x : -50, duration : 400}}" | ||
134 | + on:click={showBar}> | ||
135 | + 행사<br>목록 | ||
136 | + </button> | ||
137 | +{/if} | ||
138 | +<SideBar bind:show={sidebar_show}> | ||
139 | + | ||
140 | + {#if festaParsed.length > 0} | ||
141 | + {#each festaParsed as festa} | ||
142 | + <div class="{festa.checked ? "selected" : "festa"}" | ||
143 | + on:click={() => {check(festa.id)}}> | ||
144 | + <div class="title">{festa.title}</div> | ||
145 | + <div class="addr"><img alt="pin" src="/public/map-pin.png"><div>{festa.addr1}</div></div> | ||
146 | + </div> | ||
147 | + {/each} | ||
148 | + {:else} | ||
149 | + 개최되는 축제가 없습니다. | ||
150 | + {/if} | ||
151 | + | ||
152 | +</SideBar> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/SideItems/Info.svelte
0 → 100644
1 | +<script> | ||
2 | + import SideBar from "./SideBar.svelte" | ||
3 | + | ||
4 | + export let festa; | ||
5 | + export let sidebar_show = false; | ||
6 | + let side = "right"; | ||
7 | + | ||
8 | + function hide() { | ||
9 | + if (window.scrollY > 400) { | ||
10 | + sidebar_show = false; | ||
11 | + } | ||
12 | + } | ||
13 | +</script> | ||
14 | + | ||
15 | +<style> | ||
16 | + .title { | ||
17 | + font-size: 18pt; | ||
18 | + font-weight: bold; | ||
19 | + text-align: center; | ||
20 | + } | ||
21 | + | ||
22 | + .content { | ||
23 | + padding: 0.5rem 0.5rem 0.5rem; | ||
24 | + text-align: center; | ||
25 | + } | ||
26 | + | ||
27 | + .info img { | ||
28 | + max-height: 20rem; | ||
29 | + } | ||
30 | + | ||
31 | +</style> | ||
32 | + | ||
33 | +<svelte:window on:scroll={hide}></svelte:window> | ||
34 | +<SideBar bind:show={sidebar_show} {side}> | ||
35 | + <div class="info"> | ||
36 | + <div class="title">{festa.title}</div> | ||
37 | + <img alt="festaImg" src={festa.firstimage}><br> | ||
38 | + <div class="content"> | ||
39 | + 개최지 : {festa.addr1}<br> | ||
40 | + 전화번호 : {festa.tel}<br> | ||
41 | + <!-- 행사 시작일 : {festa.startdate}<br> | ||
42 | + 행사 시작일 : {festa.enddate}<br> | ||
43 | + 날씨 : {festa.weather 어쩌구} --> | ||
44 | + </div> | ||
45 | + </div> | ||
46 | + | ||
47 | + | ||
48 | + | ||
49 | +</SideBar> |
src/SideItems/SideBar.svelte
0 → 100644
1 | +<script> | ||
2 | + import { fly } from 'svelte/transition'; | ||
3 | + | ||
4 | + export let show = true; | ||
5 | + export let side = "left"; | ||
6 | + </script> | ||
7 | + | ||
8 | +<style> | ||
9 | + .nav { | ||
10 | + overflow-y: auto; | ||
11 | + overflow-x: hidden; | ||
12 | + } | ||
13 | + | ||
14 | + .left { | ||
15 | + position: fixed; | ||
16 | + top: 0; | ||
17 | + left: 0; | ||
18 | + height: 100%; | ||
19 | + border-right: 1px solid #aaa; | ||
20 | + background: #fff; | ||
21 | + overflow-y: auto; | ||
22 | + width: 330px; | ||
23 | + z-index: 900; | ||
24 | + white-space: pre-line; | ||
25 | + } | ||
26 | + | ||
27 | + .right { | ||
28 | + position: fixed; | ||
29 | + top: 0; | ||
30 | + right: 0; | ||
31 | + height: 100%; | ||
32 | + border-left: 1px solid #aaa; | ||
33 | + background: #fff; | ||
34 | + overflow-y: auto; | ||
35 | + width: 560px; | ||
36 | + z-index: 900; | ||
37 | + } | ||
38 | + | ||
39 | + .navtop { | ||
40 | + display: flex; | ||
41 | + width: 100%; | ||
42 | + height: 60px; | ||
43 | + background-color: white; | ||
44 | + font-size: 30pt; | ||
45 | + color: #AAAAAA; | ||
46 | + justify-content: right; | ||
47 | + padding: 5px 5px 5px; | ||
48 | + border-bottom: #999999 solid 1px; | ||
49 | + } | ||
50 | + .bg { | ||
51 | + position: fixed; | ||
52 | + display: block; | ||
53 | + width: 100vw; | ||
54 | + height: 100vw; | ||
55 | + left: 0; | ||
56 | + top: 0; | ||
57 | + background-color: rgba(0, 0, 0, 10%); | ||
58 | + z-index: 800; | ||
59 | + } | ||
60 | + | ||
61 | + .navitems { | ||
62 | + display: flex; | ||
63 | + flex-direction: column | ||
64 | + } | ||
65 | + | ||
66 | + .left .navitems { | ||
67 | + padding: 2rem 1rem 0.6rem; | ||
68 | + } | ||
69 | + | ||
70 | +</style> | ||
71 | + | ||
72 | +{#if show} | ||
73 | + {#if side == "right"} | ||
74 | + <div class="bg" on:click={() => {show = false;}}></div> | ||
75 | + {/if} | ||
76 | + <nav class={"nav " + side} transition:fly={{x: (side == "left" ? -400 : 600), opacity: 1, duration: 800}}> | ||
77 | + {#if side == "left"} | ||
78 | + <div class="navtop" on:click={() => {show = false;}} | ||
79 | + style="justify-content:{side == "left" ? "right" : "left"}; cursor:pointer;"> | ||
80 | + { side == "left" ? "<" : ">" } | ||
81 | + </div> | ||
82 | + {/if} | ||
83 | + <div class="navitems"> | ||
84 | + <slot> | ||
85 | + | ||
86 | + </slot> | ||
87 | + </div> | ||
88 | + </nav> | ||
89 | +{/if} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/Stores/AllFestas.js
0 → 100644
src/Stores/DisplayedFestas.js
0 → 100644
src/Stores/DistrictStore.js
0 → 100644
src/main.js
0 → 100644
views/error.ejs
deleted
100644 → 0
views/index.ejs
deleted
100644 → 0
webpack.config.js
0 → 100644
1 | +const path = require('path'); | ||
2 | + | ||
3 | +const mode = process.env.NODE_ENV || 'development'; | ||
4 | +const prod = mode === 'production'; | ||
5 | + | ||
6 | +module.exports = { | ||
7 | + entry: { | ||
8 | + bundle: ['./src/main.js'] | ||
9 | + }, | ||
10 | + resolve: { | ||
11 | + alias: { | ||
12 | + svelte: path.resolve('node_modules', 'svelte') | ||
13 | + }, | ||
14 | + extensions: ['.mjs', '.js', '.svelte'], | ||
15 | + mainFields: ['svelte', 'browser', 'module', 'main'] | ||
16 | + }, | ||
17 | + output: { | ||
18 | + path: __dirname + '/public/build', | ||
19 | + filename: '[name].js', | ||
20 | + chunkFilename: '[name].[id].js' | ||
21 | + }, | ||
22 | + module: { | ||
23 | + rules: [ | ||
24 | + { | ||
25 | + test: /\.svelte$/, | ||
26 | + use: { | ||
27 | + loader: 'svelte-loader', | ||
28 | + options: { | ||
29 | + emitCss: true, | ||
30 | + hotReload: true | ||
31 | + } | ||
32 | + } | ||
33 | + }, | ||
34 | + { | ||
35 | + test: /\.css$/, | ||
36 | + use: ['style-loader', 'css-loader'] | ||
37 | + } | ||
38 | + ] | ||
39 | + }, | ||
40 | + mode, | ||
41 | + plugins: [], | ||
42 | + devtool: prod ? false: 'source-map' | ||
43 | +}; |
-
Please register or login to post a comment