├── .gitignore
├── README.md
├── eslintrc.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── img
│ ├── agenda_peques_logo.svg
│ ├── agenda_peques_logo_text.svg
│ ├── cat-info.svg
│ ├── cat.svg
│ ├── events-img.svg
│ ├── footerImg.svg
│ ├── globe.svg
│ ├── img-create-event-2.svg
│ ├── img-create-event.svg
│ ├── rocket.svg
│ ├── sign-up-globe.svg
│ └── wireframe.jpg
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.test.js
├── components
├── CreateEvents.css
├── EmptyEvents.css
├── EmptyEvents.js
├── Events.css
├── Events.js
├── Footer.css
├── Footer.js
├── Header.css
├── Header.js
├── HeaderDate.css
├── HeaderDate.js
├── Info.css
├── InfoDetailEvent.css
├── ListEvents.css
├── Loading.css
├── Loading.js
├── SignIn.css
├── SignUp.css
└── icons
│ ├── IconCalendar.js
│ ├── IconClock.js
│ ├── IconDelete.js
│ ├── IconEdit.js
│ ├── IconLink.js
│ ├── IconLocation.js
│ ├── IconReset.js
│ ├── IconTarget.js
│ └── IconZoomIn.js
├── context
└── userContext.js
├── globalStyles.css
├── hooks
├── useField.js
└── useUser.js
├── pages
├── _app.js
├── createEvents
│ └── [id].js
├── eventDetail
│ └── [id].js
├── index.js
├── info.js
├── signIn.js
└── signUp.js
├── setupTests.js
└── utils
├── api.js
├── localStorage.js
├── tools.js
└── tools.test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 | .env
33 |
34 | # vercel
35 | .vercel
36 |
37 | package-lock.json
38 | yarn.lock
39 | public/workbox-*.js
40 | .prettierignore
41 | .pretierrc.js
42 | .eslintrc.js
43 | .eslintignore
44 |
45 | .next
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MIGRATED APP FROM REACT TO NEXTJS
2 |
3 | First,this project was created by ItziarZG and [Marga](https://marga.pro) with [Create React App](https://github.com/facebook/create-react-app).
4 | Then, was migrated to [NEXTJS](https://nextjs.org/) in a live streaming with [@midudev](https://midu.dev/) [here](https://www.twitch.tv/videos/1086611816)
5 |
6 |
7 | ## Getting Started
8 |
9 | First, run the development server:
10 |
11 | ```bash
12 | npm run dev
13 | # or
14 | yarn dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
20 |
21 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
22 |
23 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
24 |
25 | ## Learn More
26 |
27 | To learn more about Next.js, take a look at the following resources:
28 |
29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
31 |
32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
33 |
34 | ## Deploy on Vercel
35 |
36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
37 |
38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
39 |
--------------------------------------------------------------------------------
/eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | },
6 | extends: ["plugin:react/recommended", "standard", "prettier"],
7 | parserOptions: {
8 | ecmaFeatures: {
9 | jsx: true,
10 | },
11 | ecmaVersion: 12,
12 | sourceType: "module",
13 | },
14 | plugins: ["react"],
15 | rules: { "react/react-in-jsx-scope": "off", "react/prop-types": "off" },
16 | };
17 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src"
4 | }
5 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://www.agendapequeseivissa.tk/",
3 | "name": "agenda-react",
4 | "version": "0.1.0",
5 | "private": true,
6 | "dependencies": {
7 | "@supabase/supabase-js": "1.15.1",
8 | "@testing-library/jest-dom": "5.14.1",
9 | "@testing-library/react": "11.2.7",
10 | "@testing-library/user-event": "12.8.3",
11 | "next": "^12.0.1",
12 | "react": "^17.0.2",
13 | "react-dom": "17.0.2",
14 | "web-vitals": "1.1.2"
15 | },
16 | "scripts": {
17 | "dev": "next dev",
18 | "build": "next build",
19 | "start": "next start"
20 | },
21 | "browserslist": {
22 | "production": [
23 | ">0.2%",
24 | "not dead",
25 | "not op_mini all"
26 | ],
27 | "development": [
28 | "last 1 chrome version",
29 | "last 1 firefox version",
30 | "last 1 safari version"
31 | ]
32 | },
33 | "devDependencies": {
34 | "eslint": "^7.32.0",
35 | "eslint-config-next": "^11.0.1",
36 | "eslint-config-prettier": "^8.3.0",
37 | "eslint-config-standard": "^16.0.3",
38 | "eslint-plugin-import": "^2.23.4",
39 | "eslint-plugin-node": "^11.1.0",
40 | "eslint-plugin-promise": "^5.1.0",
41 | "eslint-plugin-react": "^7.24.0",
42 | "prettier": "^2.3.2"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itziarZG/agenda-react/6436ea87ca1494e804b27cf8f95a7dfcaa60dc33/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/agenda_peques_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
--------------------------------------------------------------------------------
/public/img/agenda_peques_logo_text.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 | agenda
53 |
54 |
55 | peques
56 |
57 |
58 |
--------------------------------------------------------------------------------
/public/img/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/img/rocket.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/img/sign-up-globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/img/wireframe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itziarZG/agenda-react/6436ea87ca1494e804b27cf8f95a7dfcaa60dc33/public/img/wireframe.jpg
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
17 |
23 |
24 |
25 |
29 |
30 |
39 | Agenda Peques Eivissa
40 |
41 |
42 | You need to enable JavaScript to run this app.
43 |
44 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itziarZG/agenda-react/6436ea87ca1494e804b27cf8f95a7dfcaa60dc33/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itziarZG/agenda-react/6436ea87ca1494e804b27cf8f95a7dfcaa60dc33/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | /* .App {
2 | text-align: center;
3 | } */
4 | html{
5 | scroll-behavior: smooth;
6 | }
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render( );
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/components/CreateEvents.css:
--------------------------------------------------------------------------------
1 | .form_event {
2 | max-width: 384px;
3 | width: 80%;
4 | margin: auto;
5 | padding-bottom: 20px;
6 | }
7 |
8 | .reset-Info-createEvent {
9 | background-color: var(--white);
10 | fill: var(--primary);
11 | padding: 2px 3px;
12 | border-radius: 100%;
13 | font-weight: bold;
14 | font-size: 20px;
15 | width: 40px;
16 | height: 40px;
17 | box-shadow: 3px 3px 6px var(--gray-600);
18 | /* border: 2px solid var(--primary); */
19 | margin-top: 20px;
20 | position: fixed;
21 | /* top: 100px; */
22 | right: 30px;
23 | cursor: pointer;
24 | appearance: none;
25 | -webkit-appearance: none;
26 | -moz-appearance: none;
27 | text-decoration: none;
28 | }
29 |
30 | .reset-Info-createEvent:hover {
31 | fill: var(--white);
32 | background-color: var(--primary);
33 | box-shadow: 5px 5px 6px var(--gray-600);
34 | }
35 |
36 | .create-event-box {
37 | margin-top: 20px;
38 | height: calc(100vh - 185px);
39 | overflow: auto;
40 | }
41 |
42 | .create_event_title {
43 | text-align: left;
44 | color: var(--primary);
45 | font-size: 18px;
46 | font-weight: 700;
47 | margin-top: 20px;
48 | }
49 |
50 | .event_form {
51 | display: flex;
52 | flex-direction: column;
53 | align-items: flex-start;
54 | }
55 |
56 | .event_form_input {
57 | max-width: 384px;
58 | width: 100%;
59 | font-size: 16px;
60 | font-family: inherit;
61 | font-weight: 400;
62 | line-height: 24px;
63 | box-sizing: border-box;
64 | padding: 10px 16px;
65 | outline: none;
66 | border-radius: 8px;
67 | border: none;
68 | appearance: none;
69 | color: var(--black-200);
70 | background-color: var(--gray-200);
71 | margin-bottom: 2px;
72 |
73 | }
74 | .event_form_input_file::-webkit-file-upload-button {
75 | visibility: hidden;
76 | }
77 | .event_form_input_file:before {
78 | content: 'Busca el archivo';
79 | display: inline-block;
80 | background: linear-gradient(top, #f9f9f9, #e3e3e3);
81 | border: 1px solid #999;
82 | color: var(--gray-800);
83 | border-radius: 3px;
84 | padding: 5px 8px;
85 | outline: none;
86 | white-space: nowrap;
87 | user-select: none;
88 | cursor: pointer;
89 | text-shadow: 1px 1px #fff;
90 | font-weight: 700;
91 | font-size: 10pt;
92 | }
93 | .event_form_input_file:hover::before {
94 |
95 | border-color: black;
96 | }
97 | .event_form_inputactive_file:active::before {
98 | background: -webkit-linear-gradient(top, #e3e3e3, #f9f9f9);
99 | }
100 | .event_form_input_number{
101 | width: 50%;
102 | }
103 |
104 | .event_form_label {
105 | margin: 14px 0 4px;
106 | color: var(--black-200);
107 | font-size: 14px;
108 | font-weight: 700;
109 | }
110 |
111 | .event_btn {
112 | margin-top: 20px;
113 | max-width: 384px;
114 | width: 100%;
115 | padding: 10px 16px;
116 | cursor: pointer;
117 | border: none;
118 | background: var(--primary);
119 | color: var(--white);
120 | border-radius: 8px;
121 | appearance: none;
122 | position: relative;
123 | outline: none;
124 | font-size: 14px;
125 | font-weight: 700;
126 | line-height: 20px;
127 | height: 40px;
128 | }
129 |
130 | .event_btn:disabled {
131 | opacity: 0.5;
132 | }
133 |
134 | .event_btn:disabled:hover {
135 | opacity: 0.5;
136 | }
137 |
138 | .event_btn:hover {
139 | opacity: 0.7;
140 | }
141 |
142 | @media screen and (min-width: 750px) {
143 | .create-event-box {
144 | display: flex;
145 | }
146 | .box-create-event-img {
147 | width: 30%;
148 | }
149 | .form_event {
150 | width: 70%;
151 | }
152 | .create-event-img {
153 | width: 25%;
154 | height: 100vh;
155 | background-image: url("/img/img-create-event-2.svg");
156 | background-repeat: no-repeat;
157 | background-position: center;
158 | border-radius: 20px;
159 | margin-left: 10%;
160 | position: fixed;
161 | opacity: 0.7;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/components/EmptyEvents.css:
--------------------------------------------------------------------------------
1 | .empty_events_container {
2 | width: 100vw;
3 | height: 85vh;
4 | /* background: var(--gray-500); */
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 | p{
10 | color: var(--gray-800);
11 | font-size: 16px;
12 | font-weight: 700;
13 | width: 200px;
14 | line-height: 1.5;
15 | }
16 | .loading_img {
17 | height: 30%;
18 | }
19 |
20 | .eyes-empty {
21 | fill: var(--white);
22 | }
23 |
24 | .eyes {
25 | fill: var(--black);
26 | animation-name: empty;
27 | animation-duration: 2s;
28 | animation-iteration-count: infinite;
29 | }
30 |
31 | .eyes-Agenda {
32 | fill: white;
33 | }
34 |
35 | @keyframes empty {
36 | 0% {
37 | fill: var(--black);
38 | }
39 | 50% {
40 | fill: var(--black);
41 | }
42 | 100% {
43 | fill: var(--white);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/EmptyEvents.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const EmptyEvents = () => {
4 | return (
5 |
6 |
23 |
24 |
28 |
29 |
30 |
37 |
38 |
39 |
43 |
44 |
45 |
49 |
50 |
51 |
52 |
59 |
60 |
61 |
68 |
69 |
70 |
77 |
78 |
79 |
86 |
87 |
88 |
95 |
96 |
97 |
104 |
105 |
106 |
113 |
114 |
115 |
122 |
123 |
124 |
131 |
132 |
133 |
140 |
141 |
146 |
150 |
151 |
152 |
153 |
Els events ja han passat. Afegeix un nou!
154 |
155 | );
156 | };
157 |
158 | export default EmptyEvents;
159 |
--------------------------------------------------------------------------------
/src/components/Events.css:
--------------------------------------------------------------------------------
1 | .event_title {
2 | font-size: 16px;
3 | font-weight: 700;
4 | /* max-width: 150px; */
5 | overflow: auto;
6 | text-align: left;
7 | line-height: 20px;
8 | }
9 | .eventsTime {
10 | height: 540px;
11 | }
12 |
13 | .event_date {
14 | font-size: 14px;
15 | color: var(--gray-800);
16 | font-weight: bold;
17 | }
18 | .event_date::after{
19 | content: "h";
20 | display: inline-block;
21 |
22 | }
23 |
24 | .event {
25 | width: 90%;
26 | margin: auto;
27 | padding: 10px 10px 25px 10px;
28 | display: flex;
29 | flex-direction: row;
30 | align-items: flex-start;
31 | justify-content: flex-start;
32 | /* box-shadow: #d1d9e6 2px 2px 5px; */
33 | /* cursor: pointer; */
34 | }
35 |
36 | /* .event:hover {
37 | box-shadow: #d1d9e6 5px 5px 10px;
38 | } */
39 |
40 | .event_box_1 {
41 | display: flex;
42 | width: 70%;
43 | overflow-wrap: break-word;
44 | word-wrap: break-word;
45 | }
46 |
47 | .more_delete_btns {
48 | display: flex;
49 | justify-content: flex-end;
50 | align-items: flex-end;
51 | /* background-color: blueviolet; */
52 | margin-top: 25px;
53 | }
54 |
55 | .icon_delete {
56 | /* width: 20px; */
57 | width: 25px;
58 | margin-left: 5px;
59 | fill: var(--gray-800);
60 | cursor: pointer;
61 | }
62 |
63 | .icon_edit {
64 | /* width: 20px; */
65 | width: 25px;
66 | margin-left: 25px;
67 | fill: var(--gray-800);
68 | cursor: pointer;
69 | }
70 |
71 | .icon_edit:hover {
72 | fill: var(--primary);
73 | }
74 |
75 | .icon_delete:hover {
76 | fill: var(--primary);
77 | }
78 |
79 | .event_box_2 {
80 | display: flex;
81 | flex-direction: column;
82 | align-items: flex-end;
83 | /* justify-content: space-between; */
84 | width: 30%;
85 | /* background-color: brown; */
86 | }
87 |
88 | .event_img {
89 | width: 70px;
90 | height: 70px;
91 | object-fit: cover;
92 | border-radius: 10%;
93 | margin-right: 10px;
94 | }
95 |
96 | .more_info {
97 | color: var(--white);
98 | font-weight: 700;
99 | padding: 3px 12px;
100 | cursor: pointer;
101 | border: none;
102 | text-decoration: none;
103 | color: var(--primary);
104 | background-color: var(--white);
105 | border-radius: 6px;
106 | -webkit-appearance: none;
107 | appearance: none;
108 | outline: none;
109 | font-size: 14px;
110 | line-height: 20px;
111 | height: 28px;
112 | margin-top: 20px;
113 | opacity: 0.7;
114 | box-shadow: #d1d9e6 2px 2px 5px;
115 | }
116 |
117 | .more_info:hover {
118 | color: var(--white);
119 | background: var(--primary);
120 | }
121 |
122 | @media screen and (min-width: 750px) {
123 | .events {
124 | width: 500px;
125 | margin: auto;
126 | }
127 |
128 | .event_title {
129 | max-width: 300px;
130 | }
131 | }
132 |
133 | @media screen and (min-width: 1000px) {
134 | .eventsTime {
135 | display: flex;
136 | width: 100%;
137 | justify-content: center;
138 | }
139 |
140 | .event_list_1 {
141 | width: 100%;
142 | margin-right: 0;
143 | }
144 |
145 | .event_list_2 {
146 | /* background-color: green; */
147 | width: 30%;
148 | height: calc(100vh - 140px);
149 | }
150 |
151 | .events {
152 | /* width: 65%; */
153 | width: 50%;
154 | margin: 0;
155 | }
156 |
157 | .event_img {
158 | width: 100px;
159 | height: 100px;
160 | object-fit: cover;
161 | border-radius: 10%;
162 | margin-right: 10px;
163 | }
164 |
165 | .event_title {
166 | max-width: 300px;
167 | font-size: 18px;
168 | }
169 |
170 | .event_date {
171 | font-size: 16px;
172 | }
173 |
174 | .img-globe-box2 {
175 | width: 100%;
176 | height: 100%;
177 | background-image: url("/img/globe.svg");
178 | background-repeat: no-repeat;
179 | background-position: center;
180 | margin: 0 55px;
181 |
182 | margin-left: 0;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/components/Events.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useRouter } from "next/router";
3 | import Link from "next/link";
4 |
5 | import api from "../utils/api";
6 |
7 | import HeaderDate from "./HeaderDate.js";
8 | import IconDelete from "./icons/IconDelete.js";
9 |
10 | const Events = ({ events, date, userId }) => {
11 | const history = useRouter();
12 |
13 | async function deleteEvents(id) {
14 | await api.deleteEvent(id);
15 | window.location.reload()
16 | }
17 |
18 | return (
19 |
20 |
21 | {events[date].map((ev) => {
22 | // const time = ev.hour ? ev.hour.slice(0, -3) + "h" : null;
23 | return (
24 |
25 |
26 |
27 |
{ev.title}
28 |
29 |
30 |
{ev.hour}
31 |
32 | {userId === ev.user_id && (
33 |
34 | {/*
35 |
36 |
37 |
38 | */}
39 |
deleteEvents(ev.id)}>
40 |
41 |
42 |
43 | )}
44 |
45 |
46 |
Info
47 |
48 |
49 |
50 | );
51 | })}
52 |
53 | );
54 | };
55 |
56 | export default Events;
57 |
--------------------------------------------------------------------------------
/src/components/Footer.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | -webkit-box-sizing: border-box;
6 | }
7 |
8 | .footer {
9 | width: 100%;
10 | height: 75px;
11 | padding: 30px 10px 50px;
12 | display: flex;
13 | flex-direction: row;
14 | align-items: center;
15 | justify-content: center;
16 | position: fixed;
17 | bottom: 0;
18 | }
19 |
20 | .footer_logo {
21 | width: 100px;
22 | height: 30px;
23 | background-image: url("/img/agenda_peques_logo_text.svg");
24 | background-repeat: no-repeat;
25 | background-position: start;
26 | }
27 |
28 | .footer_box1 {
29 | width: 30%;
30 | height: 70px;
31 | display: flex;
32 | flex-direction: column;
33 | align-items: flex-start;
34 | justify-content: space-around;
35 | z-index: 1;
36 | }
37 |
38 | .footer_box2 {
39 | width: 70%;
40 | height: 65px;
41 | font-size: 12px;
42 | color: var(--gray-800);
43 | text-align: end;
44 | display: flex;
45 | flex-direction: column;
46 | align-items: flex-end;
47 | justify-content: space-around;
48 | z-index: 1;
49 | }
50 |
51 | .copyright {
52 | font-size: 14px;
53 | color: var(--gray-800);
54 | }
55 |
56 | .footer_mail {
57 | font-size: 12px;
58 | color: var(--gray-800);
59 | font-weight: bold;
60 | }
61 |
62 | .footer_authors {
63 | font-weight: bold;
64 | }
65 |
66 | a {
67 | text-decoration: none;
68 | color: var(--gray-800);
69 | }
70 |
71 | .footer_detail {
72 | width: 100%;
73 | height: 45px;
74 | background-image: url("/img/footerImg.svg");
75 | background-repeat: repeat;
76 | background-position: right;
77 | position: fixed;
78 | bottom: 0;
79 | left: 0;
80 | opacity: 0.3;
81 | margin: 0;
82 | padding: 0;
83 | }
84 |
85 | .footer_info {
86 | font-weight: 700;
87 | cursor: pointer;
88 | color: var(--primary);
89 | font-size: 14px;
90 | z-index: 5;
91 | cursor: pointer;
92 | }
93 |
94 | .footer_info:hover {
95 | font-weight: 700;
96 | padding: 3px 12px;
97 | cursor: pointer;
98 | border: none;
99 | text-decoration: none;
100 | color: var(--primary);
101 | background-color: var(--white);
102 | border-radius: 6px;
103 | -webkit-appearance: none;
104 | appearance: none;
105 | outline: none;
106 | font-size: 14px;
107 | line-height: 20px;
108 | height: 28px;
109 | margin-top: 20px;
110 | opacity: 0.7;
111 | box-shadow: #d1d9e6 2px 2px 5px;
112 | }
113 |
114 | @media screen and (min-width: 1200px) {
115 | .footer {
116 | padding-left: 100px;
117 | padding-right: 100px;
118 | }
119 |
120 | .footer_detail {
121 | width: 100%;
122 | height: 50px;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Link from 'next/link'
3 |
4 | const Footer = (props) => {
5 | return (
6 |
7 |
8 |
9 |
10 |
© 2021
11 |
12 | {" "}
13 | + info
14 |
15 |
16 |
17 |
18 |
19 | Design by{" "}
20 |
21 | Marga Martinez
22 |
23 |
24 |
25 | Created by ItziarZG & Marga
26 |
27 |
28 |
agendapequeseivissa@gmail.com
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default Footer;
36 |
--------------------------------------------------------------------------------
/src/components/Header.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | -webkit-box-sizing: border-box;
6 | }
7 |
8 | .header {
9 | width: 100%;
10 | height: 100px;
11 | padding: 20px 30px;
12 | display: flex;
13 | justify-content: flex-end;
14 | align-items: center;
15 | border-bottom: var(--gray-200) 1px solid;
16 | margin: auto;
17 | }
18 |
19 | .header_create_event {
20 | margin-right: 10px;
21 | }
22 |
23 | .header_log_out {
24 | /* box-shadow: #d1d9e6 2px 2px 5px; */
25 | margin-right: 10px;
26 | padding: 10px 16px;
27 | cursor: pointer;
28 | border: none;
29 | color: var(--primary);
30 | background: var(--white);
31 | border-radius: 8px;
32 | outline: none;
33 | font-size: 14px;
34 | font-weight: 700;
35 | line-height: 20px;
36 | /* height: 40px; */
37 | }
38 |
39 | .header_log_out:hover {
40 | box-shadow: #d1d9e6 5px 5px 10px;
41 | }
42 |
43 | .avatar {
44 | width: 30px;
45 | margin-right: 10px;
46 | }
47 | .header_logo_container {
48 | margin-right: auto;
49 | }
50 |
51 | .header_logo {
52 | width: 40px;
53 | height: 40px;
54 | background-image: url("/img/agenda_peques_logo.svg");
55 | background-repeat: no-repeat;
56 | background-position: center;
57 | }
58 |
59 | .header_signIn {
60 | margin-right: 10px;
61 | }
62 |
63 | .btn {
64 | padding: 10px 16px;
65 | cursor: pointer;
66 | border: 1px solid var(--primary);
67 | background: var(--primary);
68 | color: var(--white);
69 | border-radius: 8px;
70 | outline: none;
71 | font-size: 14px;
72 | font-weight: 700;
73 | line-height: 20px;
74 | /* height: 40px; */
75 | }
76 |
77 | .btn:hover {
78 | opacity: 0.7;
79 | }
80 |
81 | .header_signUp {
82 | background: var(--white);
83 | color: var(--primary);
84 | }
85 | .header_link{
86 | color: var(--primary);
87 | font-size: 14px;
88 | font-weight: 700;
89 | line-height: 20px;
90 | margin-left: 6px
91 | }
92 |
93 | @media screen and (min-width: 410px) {
94 | .header_logo {
95 | width: 120px;
96 | height: 120px;
97 | background-image: url("/img/agenda_peques_logo_text.svg");
98 | background-repeat: no-repeat;
99 | background-position: center;
100 | }
101 | }
102 |
103 | @media screen and (min-width: 1200px) {
104 | .header {
105 | padding-left: 100px;
106 | padding-right: 100px;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import { useRouter } from "next/router";
2 | import { useContext } from 'react';
3 | import Link from "next/link";
4 | import storage from "../utils/localStorage";
5 | import userContext from "context/userContext";
6 |
7 | export default function Header() {
8 | const history = useRouter();
9 |
10 | const { user, setUser } = useContext(userContext)
11 |
12 | function logout() {
13 | setUser({ userId: null });
14 | storage.wipeUser();
15 | history.replace("/");
16 | }
17 |
18 | function renderSign() {
19 | return (
20 | <>
21 |
22 | Log in
23 |
24 |
25 | Register
26 |
27 |
28 | Info
29 |
30 | >
31 | );
32 | }
33 |
34 | function renderLogueado() {
35 | return (
36 | <>
37 |
38 | Sign out
39 |
40 |
41 | Crear evento
42 |
43 | >
44 | );
45 | }
46 |
47 | return (
48 | <>
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | {user.length === 0 || user.userId === null ? renderSign() : renderLogueado()}
57 |
58 | >
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/HeaderDate.css:
--------------------------------------------------------------------------------
1 | .header_date {
2 | width: 90%;
3 | margin: auto;
4 | }
5 |
6 | .header_date_title {
7 | font-size: 20px;
8 | margin-top: 20px;
9 | margin-bottom: 10px;
10 | padding-bottom: 10px;
11 | color: var(--gray-800);
12 | text-align: left;
13 | border-bottom: solid 2px var(--gray-200);
14 | }
15 |
16 | .header_date_day {
17 | color: var(--primary);
18 | font-weight: 800;
19 | font-size: 22px;
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/HeaderDate.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const MONTHS = [
4 | 'Enero',
5 | 'Febrero',
6 | 'Marzo',
7 | 'Abril',
8 | 'Mayo',
9 | 'Junio',
10 | 'Julio',
11 | 'Agosto',
12 | 'Septembre',
13 | 'Octubre',
14 | 'Noviembre',
15 | 'Diciembre',
16 | ];
17 |
18 | const WEEKDAYS = [
19 | 'Domingo',
20 | 'Lunes',
21 | 'Martes',
22 | 'Miercoles',
23 | 'Jueves',
24 | 'Viernes',
25 | 'Sabado',
26 | ];
27 |
28 | const HeaderDate = ({ date }) => {
29 | let dateObject = new Date(date);
30 | let month = MONTHS[dateObject.getMonth()];
31 | let weekday = WEEKDAYS[dateObject.getDay()];
32 | let day = dateObject.getDate();
33 |
34 | return (
35 |
36 |
37 | {weekday} | {day} de {month}
38 |
39 |
40 | );
41 | };
42 |
43 | export default HeaderDate;
44 |
--------------------------------------------------------------------------------
/src/components/Info.css:
--------------------------------------------------------------------------------
1 | .info_detail_footer {
2 | width: 100%;
3 | height: 40px;
4 | background-image: url("/img/footerImg.svg");
5 | background-repeat: repeat;
6 | background-position: right;
7 | position: fixed;
8 | bottom: 40px;
9 | left: 0;
10 | opacity: 0.3;
11 | margin: 0;
12 | padding: 0;
13 | /* mask-image: linear-gradient(to top, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); */
14 | }
15 | .info__texts {
16 | width: 100%;
17 | height: calc(100vh - 185px);
18 | color: var(--gray-800);
19 | font-size: 16px;
20 | font-weight: 700;
21 | margin-bottom: 10px;
22 | overflow: auto;
23 | padding: 10px;
24 | }
25 | .info__texts__text{
26 | width: 100%;
27 | }
28 | .info__link {
29 | font-weight: 700;
30 | cursor: pointer;
31 | color: var(--primary);
32 | font-size: 14px;
33 | padding-bottom: 10px;
34 | }
35 |
36 | .info__link:hover {
37 | font-weight: 700;
38 | padding: 3px 12px;
39 | cursor: pointer;
40 | border: none;
41 | text-decoration: none;
42 | color: var(--primary);
43 | background-color: var(--white);
44 | border-radius: 6px;
45 | -webkit-appearance: none;
46 | appearance: none;
47 | outline: none;
48 | font-size: 14px;
49 | line-height: 20px;
50 | height: 28px;
51 | margin-top: 20px;
52 | opacity: 0.7;
53 | box-shadow: #d1d9e6 2px 2px 5px;
54 | }
55 |
56 | .info__line {
57 | width: 100%;
58 | border-bottom: var(--gray-200) 1px solid;
59 | }
60 | .strong{
61 | font-weight: 700;
62 | color:black
63 | }
64 | @media all and (min-width: 768px) {
65 | .info__texts {
66 | padding: 30px;
67 | display: flex;
68 | flex-direction: column;
69 | align-items: center;
70 | justify-content: space-evenly;
71 | }
72 | }
73 | @media all and (min-width: 1024px) {
74 | .info__link {
75 | font-size: 18px;
76 | }
77 | .info__texts {
78 | padding: 50px;
79 | display: flex;
80 | flex-direction: column;
81 | align-items: center;
82 | justify-content: flex-start;
83 | }
84 | .info__texts__text {
85 | margin: 30px 0;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/InfoDetailEvent.css:
--------------------------------------------------------------------------------
1 | .info_detail_container {
2 | display: flex;
3 | flex-direction: column;
4 | padding: 0 20px;
5 | height: calc(100vh - 180px);
6 | overflow: auto;
7 | }
8 |
9 | .info_detail_reset {
10 | background-color: var(--white);
11 | fill: var(--primary);
12 | padding: 2px 3px;
13 | border-radius: 100%;
14 | font-weight: bold;
15 | font-size: 20px;
16 | width: 40px;
17 | height: 40px;
18 | box-shadow: 3px 3px 6px var(--gray-600);
19 | /* border: 2px solid var(--primary); */
20 | /* margin-top: 20px; */
21 | align-self: flex-end;
22 | cursor: pointer;
23 | text-decoration: none;
24 | }
25 |
26 | .info_detail_reset:hover {
27 | fill: var(--white);
28 | background-color: var(--primary);
29 | box-shadow: 5px 5px 6px var(--gray-600);
30 | }
31 |
32 | .info_detail {
33 | width: 90%;
34 | max-width: 500px;
35 | margin: auto;
36 | text-align: left;
37 | padding: 10px;
38 | display: flex;
39 | flex-direction: column;
40 | }
41 |
42 | .info_detail_text {
43 | margin: auto;
44 | width: 90%;
45 | }
46 |
47 | .info_detail_date {
48 | margin-bottom: 20px;
49 | }
50 | .info_detail_title {
51 | color: var(--primary);
52 | font-size: 26px;
53 | margin-bottom: 10px;
54 | word-wrap: break-word;
55 | line-height: 28px;
56 | }
57 |
58 | .info_detail_image {
59 | margin-bottom: 20px;
60 | margin-top: 20px;
61 | width: 90%;
62 | height: auto;
63 | border-radius: 20px;
64 | align-self: center;
65 | }
66 |
67 | .info_detail_items_date {
68 | color: var(--black);
69 | font-size: 18px;
70 | font-weight: 700;
71 | margin-bottom: 10px;
72 | line-height: 20px;
73 | word-wrap: break-word;
74 | display: flex;
75 | align-items: center;
76 | }
77 |
78 | .info_detail_items {
79 | color: var(--gray-800);
80 | font-size: 16px;
81 | font-weight: 700;
82 | margin-bottom: 10px;
83 | line-height: 20px;
84 | word-wrap: break-word;
85 | display: flex;
86 | align-items: center;
87 | }
88 |
89 | .icon-text {
90 | color: var(--gray-800);
91 | }
92 |
93 | .icon_calendar {
94 | width: 20px;
95 | fill: var(--gray-800);
96 | margin-right: 10px;
97 | }
98 |
99 | .icon_clock {
100 | width: 20px;
101 | fill: var(--gray-800);
102 | margin-right: 10px;
103 | }
104 |
105 | .icon_items {
106 | width: 18px;
107 | fill: var(--gray-800);
108 | margin-right: 10px;
109 | }
110 |
111 | .icon_info {
112 | width: 80%;
113 | display: -webkit-box;
114 | -webkit-box-orient: vertical;
115 | -webkit-line-clamp: 3;
116 | overflow: hidden;
117 |
118 | }
119 |
120 | .title-items {
121 | color: var(--gray-800);
122 | font-size: 14px;
123 | font-weight: 700;
124 | line-height: 20px;
125 | }
126 |
127 | .info_cat_img {
128 | height: 100px;
129 | background-image: url("/img/cat-info.svg");
130 | background-repeat: no-repeat;
131 | background-position: right;
132 | margin-top: 10px;
133 | margin-bottom: 20px;
134 | }
135 |
136 | @media screen and (min-width: 750px) {
137 | .info_detail_reset {
138 | align-self: flex-end;
139 | }
140 |
141 | .info_detail {
142 | flex-direction: row;
143 | max-width: 1000px;
144 | align-content: flex-start;
145 | /* margin-top: 30px; */
146 | }
147 | .info_detail_image {
148 | width: 45%;
149 | align-self: start;
150 | }
151 | .info_detail_text {
152 | margin: auto;
153 | width: 50%;
154 | margin-left: 40px;
155 | }
156 | .info_cat_img {
157 | margin-top: 0px;
158 | }
159 | }
160 |
161 | @media screen and (min-width: 900px) {
162 | .info_cat_img {
163 | height: 150px;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/components/ListEvents.css:
--------------------------------------------------------------------------------
1 | .events {
2 | /* width: 384px; */
3 | margin: auto;
4 | }
5 |
6 | @media screen and (min-width: 1000px) {
7 | .event_list_1 {
8 | width: 100%;
9 | margin-right: 0;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/Loading.css:
--------------------------------------------------------------------------------
1 | .loading_container {
2 | width: 100vw;
3 | height: 85vh;
4 | /* background: var(--gray-500); */
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | .loading_img {
11 | /* width: 40%; */
12 | height: 30%;
13 | }
14 |
15 | .eyes-Agenda {
16 | fill: white;
17 | animation-name: eyes;
18 | animation-duration: 2s;
19 | animation-iteration-count: infinite;
20 | }
21 |
22 | @keyframes eyes {
23 | 0% {
24 | fill: var(--black);
25 | }
26 | 25% {
27 | fill: var(--white);
28 | }
29 | 50% {
30 | fill: var(--black);
31 | }
32 | 100% {
33 | fill: var(--white);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Loading = () => {
4 | return (
5 |
6 |
19 |
20 |
24 |
25 |
26 |
33 |
34 |
35 |
39 |
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
61 |
62 |
63 |
71 |
72 |
73 |
80 |
81 |
82 |
89 |
90 |
91 |
98 |
99 |
100 |
107 |
108 |
109 |
116 |
117 |
118 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | );
133 | };
134 |
135 | export default Loading;
136 |
--------------------------------------------------------------------------------
/src/components/SignIn.css:
--------------------------------------------------------------------------------
1 | *:focus {
2 | outline: none;
3 | }
4 |
5 | .reset_info_signIn {
6 | background-color: var(--white);
7 | fill: var(--primary);
8 | padding: 2px 3px;
9 | border-radius: 100%;
10 | font-weight: bold;
11 | font-size: 20px;
12 | width: 40px;
13 | height: 40px;
14 | box-shadow: 3px 3px 6px var(--gray-600);
15 | /* border: 2px solid var(--primary); */
16 | margin-top: 20px;
17 | position: fixed;
18 | right: 30px;
19 | cursor: pointer;
20 | appearance: none;
21 | -webkit-appearance: none;
22 | -moz-appearance: none;
23 | text-decoration: none;
24 | }
25 |
26 | .reset_info_signIn:hover {
27 | fill: var(--white);
28 | background-color: var(--primary);
29 | box-shadow: 5px 5px 6px var(--gray-600);
30 | }
31 |
32 | .logo {
33 | width: 100px;
34 | margin-bottom: 10px;
35 | }
36 |
37 | .signIn {
38 | max-width: 384px;
39 | width: 80%;
40 | margin: auto;
41 | margin-top: 70px;
42 | }
43 |
44 | .signIn_form {
45 | max-width: 384px;
46 | width: 100%;
47 | height: 50%;
48 | display: flex;
49 | flex-direction: column;
50 | align-items: flex-start;
51 | margin: auto;
52 | }
53 |
54 | .title_signIn {
55 | margin-bottom: 20px;
56 | text-align: left;
57 | }
58 |
59 | .form_input {
60 | max-width: 384px;
61 | width: 100%;
62 | font-size: 16px;
63 | font-weight: 400;
64 | line-height: 24px;
65 | height: 40px;
66 | padding: 10px 16px;
67 | border-radius: 8px;
68 | border: none;
69 | appearance: none;
70 | color: var(--black-200);
71 | background-color: var(--gray-200);
72 | }
73 |
74 | .form_label {
75 | margin: 14px 0 4px;
76 | color: var(--black-200);
77 | font-size: 14px;
78 | font-weight: 700;
79 | }
80 |
81 | .signIn_btn {
82 | max-width: 384px;
83 | margin-top: 20px;
84 | width: 100%;
85 | padding: 10px 16px;
86 | cursor: pointer;
87 | border: none;
88 | background: var(--primary);
89 | color: var(--white);
90 | border-radius: 8px;
91 | font-size: 14px;
92 | font-weight: 700;
93 | line-height: 20px;
94 | height: 40px;
95 | appearance: none;
96 | -webkit-appearance: none;
97 | -moz-appearance: none;
98 | }
99 |
100 | .signIn_btn:hover {
101 | opacity: 0.7;
102 | }
103 |
104 | .signIn-img {
105 | width: 100%;
106 | height: 300px;
107 | /* background-color: chocolate; */
108 | display: flex;
109 | justify-content: center;
110 | align-items: center;
111 | }
112 |
113 | .signIn_img_rocket {
114 | width: 70%;
115 | margin-top: 50px;
116 | background-image: url('/img/rocket.svg');
117 | height: 100%;
118 | background-repeat: no-repeat;
119 | /* background-color: chocolate; */
120 | }
121 |
122 | @media screen and (min-width: 750px) {
123 | .signIn {
124 | max-width: 100%;
125 | height: 80vh;
126 | display: flex;
127 | flex-direction: row-reverse;
128 | justify-content: space-between;
129 | align-items: center;
130 | margin-top: 0;
131 | }
132 | .sigIn_form {
133 | width: 60%;
134 | }
135 |
136 | .title_signIn {
137 | text-align: center;
138 | }
139 |
140 | .signIn-img {
141 | width: 50%;
142 | height: 600px;
143 | }
144 |
145 | .signIn_img_rocket {
146 | width: 70%;
147 | background-image: url('/img/cat.svg');
148 | background-repeat: no-repeat;
149 | background-position: center;
150 |
151 | opacity: 0.7;
152 | }
153 | .signIn_form {
154 | margin: auto;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/components/SignUp.css:
--------------------------------------------------------------------------------
1 | *:focus {
2 | outline: none;
3 | }
4 |
5 | .logo {
6 | width: 100px;
7 | margin-bottom: 10px;
8 | }
9 |
10 | .reset-Info-SignUp {
11 | background-color: var(--white);
12 | fill: var(--primary);
13 | padding: 2px 3px;
14 | border-radius: 100%;
15 | font-weight: bold;
16 | font-size: 20px;
17 | width: 40px;
18 | height: 40px;
19 | box-shadow: 3px 3px 6px var(--gray-600);
20 | /* border: 2px solid var(--primary); */
21 | margin-top: 20px;
22 | position: fixed;
23 | right: 30px;
24 | cursor: pointer;
25 | appearance: none;
26 | -webkit-appearance: none;
27 | -moz-appearance: none;
28 | text-decoration: none;
29 | }
30 |
31 | .reset-Info-SignUp:hover {
32 | fill: var(--white);
33 | background-color: var(--primary);
34 | box-shadow: 5px 5px 6px var(--gray-600);
35 | }
36 |
37 | .signUp {
38 | max-width: 384px;
39 | width: 80%;
40 | margin: auto;
41 | margin-top: 50px;
42 | }
43 |
44 | .form {
45 | max-width: 384px;
46 | width: 100%;
47 | display: flex;
48 | flex-direction: column;
49 | align-items: flex-start;
50 | }
51 |
52 | .title_signUp {
53 | margin-bottom: 20px;
54 | text-align: left;
55 | }
56 |
57 | .form_input {
58 | width: 100%;
59 | font-size: 16px;
60 | font-weight: 400;
61 | line-height: 24px;
62 | height: 40px;
63 | padding: 10px 16px;
64 | border-radius: 8px;
65 | border: none;
66 | appearance: none;
67 | color: var(--black-200);
68 | background-color: var(--gray-200);
69 | }
70 |
71 | .form_label {
72 | margin: 14px 0 4px;
73 | color: var(--black-200);
74 | font-size: 14px;
75 | font-weight: 700;
76 | }
77 |
78 | .signUp_btn {
79 | max-width: 384px;
80 | margin-top: 20px;
81 | width: 100%;
82 | padding: 10px 16px;
83 | cursor: pointer;
84 | border: none;
85 | background: var(--primary);
86 | color: var(--white);
87 | border-radius: 8px;
88 | font-size: 14px;
89 | font-weight: 700;
90 | line-height: 20px;
91 | height: 40px;
92 | text-align: center;
93 | appearance: none;
94 | -webkit-appearance: none;
95 | -moz-appearance: none;
96 | }
97 |
98 | .signUp_btn:hover {
99 | opacity: 0.7;
100 | }
101 |
102 | .img-fish {
103 | margin-top: 10px;
104 | width: 40%;
105 | }
106 |
107 | .signUp_img {
108 | width: 100%;
109 | height: 300px;
110 | /* background-color: chocolate; */
111 | display: flex;
112 | justify-content: center;
113 | align-items: center;
114 | }
115 |
116 | .signUp_img_globe {
117 | width: 70%;
118 | margin-top: 50px;
119 | background-image: url('/img/globe.svg');
120 | height: 100%;
121 | background-repeat: no-repeat;
122 | /* background-color: chocolate; */
123 | }
124 |
125 | @media screen and (min-width: 750px) {
126 | .signUp {
127 | max-width: 100%;
128 | height: 80vh;
129 | display: flex;
130 | flex-direction: row-reverse;
131 | justify-content: space-between;
132 | align-items: center;
133 | margin-top: 0;
134 | }
135 | .form {
136 | width: 60%;
137 | }
138 |
139 | .title_signUp {
140 | text-align: center;
141 | }
142 |
143 | .signUp_img {
144 | width: 50%;
145 | height: 600px;
146 | }
147 |
148 | .signUp_img_globe {
149 | width: 70%;
150 | background-image: url('/img/sign-up-globe.svg');
151 | background-repeat: no-repeat;
152 | background-position: center;
153 |
154 | opacity: 0.7;
155 | }
156 | .form {
157 | margin: auto;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/components/icons/IconCalendar.js:
--------------------------------------------------------------------------------
1 | function IconCalendar({ className }) {
2 | return (
3 |
8 |
12 |
16 |
17 | );
18 | }
19 |
20 | export default IconCalendar;
21 |
--------------------------------------------------------------------------------
/src/components/icons/IconClock.js:
--------------------------------------------------------------------------------
1 | function IconClock({ className }) {
2 | return (
3 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | export default IconClock;
26 |
--------------------------------------------------------------------------------
/src/components/icons/IconDelete.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const IconDelete = ({ className }) => {
4 | return (
5 |
10 |
14 |
18 |
19 | );
20 | };
21 |
22 | export default IconDelete;
23 |
--------------------------------------------------------------------------------
/src/components/icons/IconEdit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const IconEdit = ({ className }) => {
4 | return (
5 |
10 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default IconEdit;
20 |
--------------------------------------------------------------------------------
/src/components/icons/IconLink.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const IconLink = ({ className }) => {
4 | return (
5 |
10 |
14 |
18 |
19 | );
20 | };
21 |
22 | export default IconLink;
23 |
--------------------------------------------------------------------------------
/src/components/icons/IconLocation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const IconLocation = ({ className }) => {
4 | return (
5 |
10 |
11 |
15 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default IconLocation;
25 |
--------------------------------------------------------------------------------
/src/components/icons/IconReset.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const IconReset = ({ className }) => {
4 | return (
5 |
10 |
15 |
16 | );
17 | };
18 |
19 | export default IconReset;
20 |
--------------------------------------------------------------------------------
/src/components/icons/IconTarget.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const IconTarget = ({ className }) => {
4 | return (
5 |
10 |
14 |
18 |
19 | );
20 | };
21 |
22 | export default IconTarget;
23 |
--------------------------------------------------------------------------------
/src/components/icons/IconZoomIn.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const IconZoomIn = ({ className }) => {
4 | return (
5 |
10 |
11 |
15 |
16 | );
17 | };
18 |
19 | export default IconZoomIn;
20 |
--------------------------------------------------------------------------------
/src/context/userContext.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, createContext } from 'react';
2 |
3 | const Context = createContext();
4 |
5 | export function UserContextProvider({ children }) {
6 | const [user, setUser] = useState([])
7 | useEffect(() => {
8 | const user = JSON.parse(localStorage.getItem("timekids-user"));
9 |
10 | if (user) {
11 | // setUserData(user);
12 | setUser(user);
13 |
14 | }
15 | }, []);
16 | return
17 | {children}
18 |
19 | }
20 |
21 | export default Context;
--------------------------------------------------------------------------------
/src/globalStyles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | * {
11 | margin: 0;
12 | padding: 0;
13 | box-sizing: border-box;
14 | }
15 |
16 | :root {
17 | --white: #ffffff;
18 | --black: #000000;
19 | --black-200: #0d0c22;
20 | --gray-200: #f1f1f2;
21 | --gray-500: #dcddde;
22 | --gray-600: #c6c8ca;
23 | --gray-800: #858686;
24 | --primary: #ea4c89;
25 | /* --secondary: #822659;
26 | --primary-light: #f8a1d1; */
27 | }
28 | a {
29 | text-decoration: none;
30 | color: var(--gray-8000);
31 | }
32 | /* code {
33 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
34 | monospace;
35 | } */
36 |
37 | /* font: bold 15px/24px 'Haas Grot Text R Web', 'Helvetica Neue', Helvetica,
38 | Arial, sans-serif; */
39 |
40 | /* font-family: 'Haas Grot Text R Web', 'Helvetica Neue', Helvetica, Arial,
41 | sans-serif; */
42 |
--------------------------------------------------------------------------------
/src/hooks/useField.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export default function useField(inputProps) {
4 | const [value, setValue] = useState("");
5 | const onChange = ({ target }) => {
6 | setValue(target.value);
7 | };
8 | return {
9 | ...inputProps,
10 | value,
11 | onChange,
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/src/hooks/useUser.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useContext } from "react";
2 | import storage from "utils/localStorage.js";
3 | import { signUp, signIn } from "utils/api";
4 | import userContext from "context/userContext";
5 |
6 | export default function useUser() {
7 | // const [userData, setUserData] = useState({ userId: null });
8 | const { user, setUser } = useContext(userContext);
9 |
10 | useEffect(() => {
11 | const user = JSON.parse(localStorage.getItem("timekids-user"));
12 |
13 | if (user) {
14 | // setUserData(user);
15 | setUser(user);
16 |
17 | }
18 | }, []);
19 |
20 | const register = (userName, password) => {
21 |
22 | return signUp(userName, password).then((resp) => {
23 | if (resp.user) {
24 | const { email, id } = resp.user;
25 | const userData = { userId: id, email };
26 | // setUserData(userData);
27 | setUser(userData);
28 | storage.setUser(userData);
29 | return { status: 200 }
30 | } else return resp.error;
31 | });
32 | };
33 | const login = (userName, password) => {
34 | return signIn(userName, password).then((resp) => {
35 |
36 | if (resp.data != null) {
37 | const { email, id } = resp.user;
38 | const userData = { userId: id, email };
39 | // setUserData(userData);
40 | setUser(userData);
41 | storage.setUser(userData);
42 | return { status: 200 }
43 | } else {
44 | return resp.error;
45 | // Error mostrarlo con toast
46 | }
47 | });
48 | };
49 |
50 | return {
51 | register,
52 | login,
53 | user,
54 | };
55 | }
56 |
--------------------------------------------------------------------------------
/src/pages/_app.js:
--------------------------------------------------------------------------------
1 | import "globalStyles.css";
2 | import "App.css";
3 | import "components/CreateEvents.css";
4 | import "components/EmptyEvents.css";
5 | import "components/Events.css";
6 | import "components/Footer.css";
7 | import "components/Header.css";
8 | import "components/HeaderDate.css";
9 | import "components/Info.css";
10 | import "components/InfoDetailEvent.css";
11 | import "components/ListEvents.css";
12 | import "components/Loading.css";
13 | import "components/SignIn.css";
14 | import "components/SignUp.css";
15 |
16 | import Head from "next/head";
17 |
18 | import Header from "components/Header.js";
19 | import { UserContextProvider } from "context/userContext";
20 |
21 | function App({ Component, pageProps }) {
22 | return (
23 | <>
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | >
34 | );
35 | }
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/src/pages/createEvents/[id].js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useContext } from "react";
2 | import { useRouter } from "next/router";
3 | import Link from "next/link";
4 | import { createEvent, getEventDetails } from "utils/api";
5 | import IconReset from "components/icons/IconReset.js";
6 | import Head from "next/head";
7 | import useField from "hooks/useField.js";
8 | import userContext from "context/userContext";
9 |
10 | export default function CreateEvents() {
11 | const { id } = useRouter().query;
12 | const history = useRouter();
13 |
14 | const { user } = useContext(userContext)
15 | const { userId } = user
16 | const [error, setErrorMessage] = useState("");
17 | const eventImgField = useField({ type: "text", name: "eventImg" });
18 | const eventNameField = useField({ type: "text", name: "eventName" });
19 | const eventLinkField = useField({ type: "text", name: "eventLink" });
20 | const eventAgeField = useField({ type: "text", name: "eventAge" });
21 | const eventCityField = useField({ type: "text", name: "eventCity" });
22 | const eventInformationField = useField({
23 | type: "text",
24 | name: "eventInformation",
25 | });
26 | const eventHourField = useField({ type: "text", name: "eventHour" });
27 | const eventDateField = useField({ type: "date", name: "eventDate" });
28 | const eventUrlField = useField({ type: "text", name: "eventUrl" });
29 | // const eventFileField = useField({ type: "file", name: "eventFile" });
30 | const [eventData, setEventData] = useState({
31 | title: "",
32 | imageUrl: "",
33 | date: "",
34 | url: "",
35 | age: 0,
36 | city: "",
37 | info: "",
38 | hour: "",
39 | user_id: userId,
40 | });
41 |
42 | useEffect(() => {
43 | if (id !== "new" && id !== undefined) {
44 | getEventDetails(id).then(({ data }) => {
45 |
46 | setEventData({
47 | title: data[0].title,
48 | imageUrl: data[0].image,
49 | date: data[0].date,
50 | url: data[0].url,
51 | age: data[0].age,
52 | city: data[0].city,
53 | info: data[0].info,
54 | hour: data[0].hour,
55 | user_id: userId,
56 | });
57 | });
58 | } else if (id === "new") {
59 | setEventData({
60 | title: "",
61 | imageUrl: "",
62 | date: "",
63 | url: "",
64 | age: 0,
65 | city: "",
66 | info: "",
67 | hour: "",
68 | user_id: userId,
69 | });
70 | }
71 | }, [id]);
72 |
73 | const handleFormEvent = (ev) => {
74 | ev.preventDefault();
75 | const data = {
76 | title: eventNameField.value,
77 | imageUrl: eventImgField.value,
78 | date: eventDateField.value,
79 | url: eventLinkField.value,
80 | age: eventAgeField.value,
81 | city: eventCityField.value,
82 | info: eventInformationField.value,
83 | hour: eventHourField.value,
84 | user_id: userId,
85 | }
86 | setEventData(data);
87 |
88 | createEvent(data)
89 | .then((resp) => {
90 |
91 | if (resp.error) {
92 | setErrorMessage(resp.error.message);
93 | setTimeout(() => setErrorMessage(""), 3000);
94 | } else if (resp.status === 201) {
95 | history.push("/");
96 | }
97 | })
98 | .catch((error) => setErrorMessage(error.message));
99 | };
100 | const updateEvent = () => {
101 |
102 | // setEventData({
103 | // title:eventData.title,
104 | // image:eventData.image,
105 | // date:eventData.date,
106 | // url:eventData.url,
107 | // age:eventData.age,
108 | // city:eventData.city,
109 | // info:eventData.info,
110 | // hour:eventData.hour,
111 | // user_id: userId,
112 | // });
113 | };
114 | const isSubmitDisabled =
115 | !eventNameField.value || !eventHourField.value || !eventDateField.value;
116 |
117 | return (
118 | <>
119 |
120 | Agenda Peques - Crear evento
121 |
126 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
142 |
143 |
Crear Evento
144 |
232 |
233 |
234 | >
235 | );
236 | }
237 |
--------------------------------------------------------------------------------
/src/pages/eventDetail/[id].js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, } from "react";
2 | import Link from "next/link";
3 | import { useRouter } from "next/router";
4 | import IconCalendar from "components/icons/IconCalendar.js";
5 | import IconClock from "components/icons/IconClock.js";
6 | import IconLocation from "components/icons/IconLocation.js";
7 | import IconLink from "components/icons/IconLink.js";
8 | import IconZoomIn from "components/icons/IconZoomIn.js";
9 | import IconTarget from "components/icons/IconTarget.js";
10 | import IconReset from "components/icons/IconReset.js";
11 | import { formatDate } from "utils/tools";
12 | import { getEventDetails } from "utils/api";
13 |
14 | const InfoDetailEvent = () => {
15 | const { id } = useRouter().query;
16 | const [event, setEvent] = useState({});
17 | const history = useRouter();
18 |
19 | useEffect(() => {
20 | if (id) getEventDetails(id).then(({ data }) => setEvent(data[0]));
21 | else history.replace("/");
22 | }, [id]);
23 |
24 | const age = event.age ? event.age : "- -";
25 | const city = event.city ? event.city : "- -";
26 | const information = event.info ? event.info : "- -";
27 | const url = event.url ? event.url : "- -";
28 | const hour = event.hour || "- -";
29 | // const hour = event.hour ? event.hour.slice(0, -3) : "- -";
30 | const date = formatDate(event.date);
31 | // const date = date1 ? date1 : "- -";
32 |
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
50 |
{event.title}
51 |
55 |
56 |
57 |
{hour} horas
58 |
59 |
60 |
61 |
65 |
66 |
67 |
68 |
{age} años
69 |
70 |
71 |
72 |
73 |
{information}
74 |
75 |
76 |
82 |
83 |
84 |
85 |
86 | );
87 | };
88 |
89 | export default InfoDetailEvent;
90 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useContext } from "react";
2 | import Head from "next/head";
3 |
4 | import { getEventsFromDate } from "utils/api";
5 | import { getTodayDate, groupByDate, sortObject } from "utils/tools";
6 | import EmptyEvents from "components/EmptyEvents.js";
7 | import Events from "components/Events.js";
8 | import Loading from "components/Loading.js";
9 | import userContext from "context/userContext";
10 |
11 | const LOADING_STATES = {
12 | empty: "empty",
13 | loading: "loading",
14 | };
15 |
16 | const ListEvents = () => {
17 | const { user, setUser } = useContext(userContext);
18 |
19 | const [events, setEvents] = useState([]);
20 | const [status, setStatus] = useState(LOADING_STATES.loading);
21 | const [errorMessage, setErrorMessage] = useState("");
22 |
23 | useEffect(() => {
24 | const today = getTodayDate();
25 | getEventsFromDate(today).then((resp) => {
26 | if (resp.status === 200) {
27 | const { data } = resp;
28 | const grouped = groupByDate(data);
29 | const sorted = sortObject(grouped);
30 | setEvents(sorted);
31 |
32 | const newStatus = data.length <= 0 ? "empty" : "loaded";
33 | setStatus(newStatus);
34 | } else {
35 | setErrorMessage("Ha habido algún error. Vuelve a cargar la página. ");
36 | }
37 | });
38 | }, []);
39 |
40 | function renderEvents() {
41 | const dates = Object.keys(events);
42 | return dates.map((date) => {
43 | return (
44 |
45 |
46 |
47 | );
48 | });
49 | }
50 |
51 | if (status === LOADING_STATES.loading) {
52 | return ;
53 | }
54 |
55 | if (status === LOADING_STATES.empty) {
56 | return ;
57 | }
58 |
59 | return (
60 | <>
61 |
62 | Agenda Peques Eivissa
63 |
64 |
70 |
71 |
72 |
75 | {errorMessage !== "" ? (
76 |
{errorMessage}
77 | ) : (
78 | <>>
79 | )}
80 |
{renderEvents()}
81 |
82 | >
83 | );
84 | };
85 |
86 | export default ListEvents;
87 |
--------------------------------------------------------------------------------
/src/pages/info.js:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import Link from "next/link";
3 |
4 | export default function Info() {
5 | return (
6 | <>
7 |
8 | Agenda Peques - Info del proyecto
9 |
14 |
20 |
21 |
22 |
23 |
Volver a Eventos
24 |
25 |
26 |
27 | CAT - Hola. Volia, simplement, explicar-te què és i el perquè d'aquest
28 | projecte. Aquesta web surt de la meva pròpia necessitat
29 | d'assabentar-me dels esdeveniments que hi ha a l'illa per a anar amb
30 | la meva família, especialment amb els meus fills petits. Durant uns
31 | anys he buscat en agendes en línia de l'illa i no he trobat cap que
32 | compti amb esdeveniments infantils especialment o són massa
33 | especialitzades en esdeveniments concrets (escoles o biblioteques) o
34 | en població concreta (Ajuntaments). La majoria de vegades és per boca
35 | a boca i per això aquest projecte està pensat perquè les famílies ens
36 | ajudem entre nosaltres a trobar esdeveniments infantils. La idea és
37 | que quan sapiguem d'un esdeveniment infantil a l'illa el publiquem
38 | qualsevol de nosaltres. Aquesta és la raó d'haver de donar-te d'alta,
39 | perquè la teva puguis publicar esdeveniments, i com que és una web de
40 | contingut infantil, hi hagi un mínim control queda guardat un
41 | identificador d'usuari per si algun contingut no fos adequat,
42 | cancel·lar aquest compte. No crec se de el cas però crec que haig de
43 | protegir els nens que puguin entrar en aquesta web. No crec que aquest
44 | projecte creixi tant com per a necessitar una validació anterior però
45 | si fes falta, es faria... Si ets professional del sector, pots
46 | donar-te d'alta i publicar les teves bitlles, estarem encantad@s! Si
47 | ets Ajuntament, biblioteca o col·legi, també ets més que benvingut.
48 |
49 |
50 |
51 | CAST -Quería, simplemente, explicarte qué es y el porqué de este
52 | proyecto. Esta web sale de mi propia necesidad de enterarme de los
53 | eventos que hay en la isla para ir con mi familia, especialmente con
54 | mis hijos pequeños. Durante unos años he buscado en agendas online de
55 | la isla y no he encontrado ninguna que cuente con eventos infantiles
56 | especialmente o son demasiado especializadas en eventos concretos
57 | (escuelas o bibliotecas) o en población concreta (ayuntamientos). La
58 | mayoría de veces es por boca a boca y por eso este proyecto está
59 | pensado para que las familias nos ayudemos entre nosotros a encontrar
60 | eventos infantiles. La idea es que cuando sepamos de un evento
61 | infantil lo publiquemos.Esta es la razón de tener que darte de alta,
62 | para que tu puedas publicar eventos, y puesto que es una web de
63 | contenido infantil, haya un mínimo control queda guardado un
64 | identificador de usuario por si algún contenido no fuera adecuado,
65 | cancelar esa cuenta. No creo se de el caso pero creo que debo proteger
66 | a los peques que puedan entrar en esta web. No creo que este proyecto
67 | crezca tanto como para necesitar una validación anterior pero si
68 | hiciera falta, se haría... Si eres profesional del sector, puedes
69 | darte de alta y publicar tus bolos, estaremos encantad@s! Si eres
70 | Ayuntamiento, biblioteca o colegio, también eres más que bienvenido.
71 |
72 |
73 | >
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/pages/signIn.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useRouter } from "next/router";
3 | import Link from "next/link";
4 | import IconReset from "components/icons/IconReset.js";
5 | import Head from "next/head";
6 | import useUser from "hooks/useUser.js";
7 | import useField from "hooks/useField.js";
8 |
9 | const SignIn = () => {
10 | const userNameField = useField({ type: "text", name: "username" });
11 | const passwordField = useField({ type: "password", name: "password" });
12 | const [errorMessage, setErrorMessage] = useState("");
13 |
14 | const {
15 | login,
16 | // userData: { userId },
17 | } = useUser();
18 | const history = useRouter();
19 |
20 | function handleFormSignIn(ev) {
21 | ev.preventDefault();
22 |
23 | login(userNameField.value, passwordField.value)
24 | .then((resp) => {
25 |
26 | if (resp.status === 200) {
27 | history.push("/");
28 | } else {
29 | setErrorMessage(resp.message);
30 | setTimeout(() => setErrorMessage(""), 3000);
31 | }
32 | })
33 | }
34 |
35 | return (
36 | <>
37 |
38 | Agenda Peques - Iniciar sesión
39 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
89 | >
90 | );
91 | };
92 |
93 | export default SignIn;
94 |
--------------------------------------------------------------------------------
/src/pages/signUp.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useRouter } from "next/router";
3 | import Link from "next/link";
4 | import IconReset from "components/icons/IconReset.js";
5 |
6 | import Head from "next/head";
7 | import useUser from "hooks/useUser.js";
8 | import useField from "hooks/useField.js";
9 |
10 | const SignUp = () => {
11 | const { register } = useUser();
12 | const userNameField = useField({ type: "text", name: "username" });
13 | const userPasswField = useField({ type: "password", name: "userpassw" });
14 | const confirmPasswField = useField({
15 | type: "password",
16 | name: "confirmpassw",
17 | });
18 |
19 | const [error, setErrorMessage] = useState("");
20 |
21 | const history = useRouter();
22 |
23 | function handleFormSignUp(ev) {
24 | ev.preventDefault();
25 |
26 | if (userPasswField.value === confirmPasswField.value) {
27 |
28 | register(userNameField.value, userPasswField.value).then((resp) => {
29 | if (resp.status === 200) {
30 | history.push("/");
31 | } else {
32 | setErrorMessage(resp.message);
33 | setTimeout(() => setErrorMessage(""), 3000);
34 | }
35 | });
36 | } else {
37 | setErrorMessage("Passwords Diferentes");
38 | setTimeout(() => setErrorMessage(""), 3000);
39 | }
40 | }
41 |
42 | // const disableButton =
43 | // !userNameField ||
44 | // userNameField.length < 3 ||
45 | // !userPasswField ||
46 | // !confirmPassword;
47 |
48 | return (
49 | <>
50 |
51 | Agenda Peques - Crear nueva cuenta
52 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
110 | >
111 | );
112 | };
113 |
114 | export default SignUp;
115 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/utils/api.js:
--------------------------------------------------------------------------------
1 | import { createClient } from "@supabase/supabase-js";
2 |
3 | const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
4 | const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY;
5 |
6 | const supabase = createClient(supabaseUrl, supabaseKey);
7 |
8 | const api = {};
9 |
10 | export function createEvent(eventData) {
11 | return supabase.from("events").insert([eventData]);
12 | }
13 |
14 | api.updateEvent = async function (eventId, eventData) {
15 | const { data } = await supabase
16 | .from("events")
17 | .update(eventData)
18 | .eq("id", eventId);
19 |
20 | return data;
21 | };
22 |
23 | api.deleteEvent = async function (eventId) {
24 | await supabase.from("events").delete().eq("id", eventId);
25 | };
26 |
27 | export function getEventDetails(eventId) {
28 | return supabase.from("events").select("*").eq("id", eventId);
29 | }
30 |
31 | export function getEventsFromDate(date) {
32 | return (
33 | supabase
34 | .from("events")
35 | .select("*")
36 | // filtrar eventos a partir de la fecha que le pongamos
37 | .gte("date", date)
38 | );
39 | }
40 |
41 | export function signIn(email, password) {
42 |
43 | return supabase.auth.signIn({
44 | email,
45 | password,
46 | });
47 | }
48 |
49 | export function signUp(email, password) {
50 |
51 | return supabase.auth.signUp({
52 | email,
53 | password,
54 | });
55 | }
56 |
57 | export default api;
58 |
--------------------------------------------------------------------------------
/src/utils/localStorage.js:
--------------------------------------------------------------------------------
1 | const LOCAL_STORAGE_USER_KEY = "timekids-user";
2 |
3 | const storage = {
4 | getUser: () => JSON.parse(localStorage.getItem(LOCAL_STORAGE_USER_KEY)),
5 | setUser: (userData) => {
6 | localStorage.setItem(LOCAL_STORAGE_USER_KEY, JSON.stringify(userData));
7 | },
8 | wipeUser: () => {
9 | localStorage.removeItem(LOCAL_STORAGE_USER_KEY);
10 | },
11 | };
12 |
13 | export default storage;
14 |
--------------------------------------------------------------------------------
/src/utils/tools.js:
--------------------------------------------------------------------------------
1 | export function groupByDate(arr) {
2 | let groups = {};
3 | if (arr) {
4 | for (let i = 0; i < arr.length; i++) {
5 | const date = arr[i].date;
6 |
7 | if (!groups[date]) {
8 | groups[date] = [arr[i]];
9 | } else {
10 | groups[date].push(arr[i]);
11 | }
12 | }
13 | }
14 | return groups;
15 | }
16 |
17 | // https://stackoverflow.com/a/29622653
18 | export const sortObject = (o) =>
19 | Object.keys(o)
20 | .sort()
21 | // eslint-disable-next-line no-sequences
22 | .reduce((r, k) => ((r[k] = o[k]), r), {});
23 |
24 | export function getTodayDate() {
25 | const today = new Date();
26 | const month = today.getMonth() + 1;
27 | const day = today.getDate();
28 |
29 | const formattedMonth = month < 10 ? "0" + month : month;
30 | const formattedDay = day < 10 ? "0" + day : day;
31 |
32 | const date = `${today.getFullYear()}-${formattedMonth}-${formattedDay}`;
33 |
34 | return date;
35 | }
36 |
37 | export function formatDate(str) {
38 | const date = new Date(str);
39 | const month = date.getMonth() + 1;
40 | const day = date.getDate();
41 |
42 | const formattedMonth = month < 10 ? "0" + month : month;
43 | const formattedDay = day < 10 ? "0" + day : day;
44 |
45 | return `${formattedDay}-${formattedMonth}-${date.getFullYear()}`;
46 | }
47 |
--------------------------------------------------------------------------------
/src/utils/tools.test.js:
--------------------------------------------------------------------------------
1 | import { groupByDate } from './tools';
2 |
3 | const ungrouped = [
4 | {
5 | name: 'Evento 2',
6 | date: '2021-04-29',
7 | },
8 | {
9 | name: 'Evento 4',
10 | date: '2022-03-02',
11 | },
12 | {
13 | name: 'Evento 1',
14 | date: '2021-03-02',
15 | },
16 | {
17 | name: 'Evento 3',
18 | date: '2021-04-29',
19 | },
20 | ];
21 |
22 | const grouped = {
23 | '2021-04-29': [
24 | {
25 | name: 'Evento 2',
26 | date: '2021-04-29',
27 | },
28 | {
29 | name: 'Evento 3',
30 | date: '2021-04-29',
31 | },
32 | ],
33 | '2022-03-02': [
34 | {
35 | name: 'Evento 4',
36 | date: '2022-03-02',
37 | },
38 | ],
39 | '2021-03-02': [
40 | {
41 | name: 'Evento 1',
42 | date: '2021-03-02',
43 | },
44 | ],
45 | };
46 |
47 | describe('groupByDate', () => {
48 | test('it groups objects by date', () => {
49 | const want = grouped;
50 | const got = groupByDate(ungrouped);
51 |
52 | expect(got).toEqual(want);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------