create-doma-react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +30 -0
- package/package.json +15 -0
- package/template/.env +7 -0
- package/template/README.md +362 -0
- package/template/eslint.config.js +39 -0
- package/template/index.html +13 -0
- package/template/package-lock.json +4774 -0
- package/template/package.json +29 -0
- package/template/public/vite.svg +1 -0
- package/template/src/App.css +0 -0
- package/template/src/App.jsx +33 -0
- package/template/src/assets/react.svg +1 -0
- package/template/src/index.css +0 -0
- package/template/src/main.jsx +11 -0
- package/template/src/pages/AddData.jsx +125 -0
- package/template/src/pages/Home.jsx +23 -0
- package/template/src/pages/Meals.jsx +44 -0
- package/template/vite.config.js +11 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Kiolvassa a nevet, amit a parancs után írsz (pl: npx create-doma-react teszt-app)
|
|
7
|
+
const projectName = process.argv[2];
|
|
8
|
+
|
|
9
|
+
if (!projectName) {
|
|
10
|
+
console.error('Kérlek add meg a projekt nevét: npx create-doma-react <projekt-nev>');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
15
|
+
const templateDir = path.join(__dirname, '../template');
|
|
16
|
+
|
|
17
|
+
console.log(`🚀 React template létrehozása itt: ${targetDir}...`);
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Bemásolja a kész React projektet az új mappába
|
|
21
|
+
fs.copySync(templateDir, targetDir);
|
|
22
|
+
|
|
23
|
+
console.log('\n✅ Sikeres másolás!');
|
|
24
|
+
console.log(`\nFuttasd ezeket a parancsokat a kezdéshez:`);
|
|
25
|
+
console.log(` cd ${projectName}`);
|
|
26
|
+
console.log(` npm install`);
|
|
27
|
+
console.log(` npm run dev (vagy npm start)`);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error('Hiba történt a másolás során:', err);
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-doma-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React template starter",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-doma-react": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": ["react", "template", "cli"],
|
|
10
|
+
"author": "Doma",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"fs-extra": "^11.2.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/template/.env
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
////////////////STÍLUS (GUN)////////////////
|
|
2
|
+
Details.jsx
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { useParams, Link } from 'react-router'
|
|
5
|
+
|
|
6
|
+
const Details = () => {
|
|
7
|
+
const { id } = useParams()
|
|
8
|
+
const [gun, setGun] = useState(null)
|
|
9
|
+
const [error, setError] = useState(null)
|
|
10
|
+
const [loading, setLoading] = useState(true)
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const fetchDetails = async () => {
|
|
14
|
+
try {
|
|
15
|
+
const res = await fetch(`http://localhost:3000/api/guns/${id}`)
|
|
16
|
+
if (!res.ok) throw new Error('Nem sikerült betölteni az adatokat.')
|
|
17
|
+
const data = await res.json()
|
|
18
|
+
setGun(data)
|
|
19
|
+
} catch (err) {
|
|
20
|
+
setError(err.message)
|
|
21
|
+
} finally {
|
|
22
|
+
setLoading(false)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
fetchDetails()
|
|
26
|
+
}, [id])
|
|
27
|
+
|
|
28
|
+
if (loading) return <p className="text-center mt-4">Betöltés...</p>
|
|
29
|
+
if (error) return <p className="text-danger text-center mt-4">{error}</p>
|
|
30
|
+
if (!gun) return null
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="container mt-4">
|
|
34
|
+
<h2 className="mb-4">Részletek</h2>
|
|
35
|
+
<div className="card">
|
|
36
|
+
{gun.image && (
|
|
37
|
+
<img src={gun.image} className="card-img-top" alt={gun.name} style={{ maxHeight: '300px', objectFit: 'contain' }} />
|
|
38
|
+
)}
|
|
39
|
+
<div className="card-body">
|
|
40
|
+
<h5 className="card-title">{gun.name}</h5>
|
|
41
|
+
<ul className="list-group list-group-flush">
|
|
42
|
+
<li className="list-group-item"><strong>Típus:</strong> {gun.type}</li>
|
|
43
|
+
<li className="list-group-item"><strong>Gyártó:</strong> {gun.manufacturer}</li>
|
|
44
|
+
<li className="list-group-item"><strong>Kaliber:</strong> {gun.caliber}</li>
|
|
45
|
+
</ul>
|
|
46
|
+
</div>
|
|
47
|
+
<div className="card-footer">
|
|
48
|
+
<Link to="/" className="btn btn-secondary">Vissza a főoldalra</Link>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default Details
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
Home.jsx
|
|
59
|
+
import { Link } from 'react-router'
|
|
60
|
+
|
|
61
|
+
const Home = ({ data }) => {
|
|
62
|
+
return (
|
|
63
|
+
<div className='container'>
|
|
64
|
+
<Link className='btn btn-primary mb-3 mt-3' to={"/felvisz"}>Új fegyver hozzáadása</Link>
|
|
65
|
+
<div className='row'>
|
|
66
|
+
{data.map((i) => (
|
|
67
|
+
<div className="card m-2" style={{ maxWidth: '18rem' }} key={i.id}>
|
|
68
|
+
<img src={i.image} className="card-img-top" alt={i.name} style={{ objectFit: 'contain' }}></img>
|
|
69
|
+
<div className="card-body">
|
|
70
|
+
<h5 className="card-title">{i.name}</h5>
|
|
71
|
+
<Link className="btn btn-primary" to={`/details/${i.id}`}>Részletes leírás</Link>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default Home
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
////////////////MŰKÖDÉS (AUTÓK)////////////////
|
|
84
|
+
Home.jsx
|
|
85
|
+
import { useEffect, useState } from 'react';
|
|
86
|
+
import { Link } from 'react-router'
|
|
87
|
+
|
|
88
|
+
const Home = ({ gyartok }) => {
|
|
89
|
+
const [adatok, setAdatok] = useState([])
|
|
90
|
+
const [id, setId] = useState(null)
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const fetchData = async () => {
|
|
94
|
+
const res = await fetch(`https://caroftheyear2023.jedlik.cloud/api/cars/${id}`)
|
|
95
|
+
const data = await res.json()
|
|
96
|
+
setAdatok(data)
|
|
97
|
+
};
|
|
98
|
+
fetchData();
|
|
99
|
+
}, [id]);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div className='container mt-3'>
|
|
103
|
+
<select id='select' name='select' onChange={e => setId(e.target.value)}>
|
|
104
|
+
<option>--- Válassz járművet ---</option>
|
|
105
|
+
{gyartok.map((g) => (
|
|
106
|
+
<option key={g.id} value={g.id} >{g.name}</option>
|
|
107
|
+
))}
|
|
108
|
+
</select>
|
|
109
|
+
<table className="table">
|
|
110
|
+
<thead>
|
|
111
|
+
<tr>
|
|
112
|
+
<th scope="col">Manufacturer name</th>
|
|
113
|
+
<th scope="col">Model</th>
|
|
114
|
+
<th scope="col">Description</th>
|
|
115
|
+
<th scope="col">Links</th>
|
|
116
|
+
</tr>
|
|
117
|
+
</thead>
|
|
118
|
+
<tbody>
|
|
119
|
+
{adatok.map((i) => (
|
|
120
|
+
<tr key={i.id}>
|
|
121
|
+
<td>{i.manufacturerName}</td>
|
|
122
|
+
<td>{i.model}</td>
|
|
123
|
+
<td>{i.description}</td>
|
|
124
|
+
<td>
|
|
125
|
+
<Link to={`/voting/${i.id}`} className="btn btn-primary">Vote</Link>
|
|
126
|
+
</td>
|
|
127
|
+
</tr>
|
|
128
|
+
))}
|
|
129
|
+
</tbody>
|
|
130
|
+
</table>
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default Home
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
Felvisz.jsx
|
|
139
|
+
import { useNavigate, useParams } from 'react-router'
|
|
140
|
+
import { Formik, Field, Form, ErrorMessage } from 'formik'
|
|
141
|
+
|
|
142
|
+
const Felvisz = ({ autok }) => {
|
|
143
|
+
const nav = useNavigate()
|
|
144
|
+
const { id } = useParams()
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<Formik
|
|
148
|
+
initialValues={{
|
|
149
|
+
carId: id || 0,
|
|
150
|
+
email: "torok.boglarka@elevel.hu",
|
|
151
|
+
comment: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore ex laborum possimus numquam corporis, nulla error impedit illo iusto quibusdam animi eius, nemo dicta velit sint consectetur magnam. Provident, ipsum.",
|
|
152
|
+
checkbox: false
|
|
153
|
+
}}
|
|
154
|
+
validate={(values) => {
|
|
155
|
+
const errors = {}
|
|
156
|
+
if (!values.carId || values.carId <= 0) errors.carId = 'Az autó megadása'
|
|
157
|
+
if (!values.email.trim()) errors.email = 'Az email megadása kötelező.'
|
|
158
|
+
if (values.checkbox == false) errors.checkbox = 'Fogadd el a felhasználási szabályzatunkat!'
|
|
159
|
+
return errors
|
|
160
|
+
}}
|
|
161
|
+
onSubmit={async (values, { setStatus, setSubmitting }) => {
|
|
162
|
+
try {
|
|
163
|
+
const res = await fetch('https://caroftheyear2023.jedlik.cloud/api/vote', {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: { 'Content-Type': 'application/json' },
|
|
166
|
+
body: JSON.stringify(values)
|
|
167
|
+
})
|
|
168
|
+
const dataRes = await res.json()
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
setStatus(dataRes.message || 'Hiba a felvitel során')
|
|
171
|
+
setSubmitting(false)
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
alert('Your vote has been sucessfully registered')
|
|
175
|
+
nav('/')
|
|
176
|
+
} catch (err) {
|
|
177
|
+
setStatus(`Szerverhiba történt. ${err.message}`)
|
|
178
|
+
setSubmitting(false)
|
|
179
|
+
}
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
{({ isSubmitting, status }) => (
|
|
183
|
+
<div className='container'>
|
|
184
|
+
<h2 className='my-3 text-center'>Vote</h2>
|
|
185
|
+
<Form className='w-50 mx-auto'>
|
|
186
|
+
{status && <p className="text-danger">{status}</p>}
|
|
187
|
+
|
|
188
|
+
<div className="mb-3">
|
|
189
|
+
<label htmlFor="carId" className="form-label">Autók</label>
|
|
190
|
+
<Field as="select" id="carId" name="carId" className="form-control">
|
|
191
|
+
<option value="0">Válassz csoportot</option>
|
|
192
|
+
{autok.map((g) => (
|
|
193
|
+
<option key={g.id} value={g.id}>{g.model}</option>
|
|
194
|
+
))}
|
|
195
|
+
</Field>
|
|
196
|
+
<ErrorMessage name="carId" component="div" className="text-danger" />
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div className="mb-3">
|
|
200
|
+
<label htmlFor="email" className="form-label">Email</label>
|
|
201
|
+
<Field type="text" id="email" name="email" className="form-control" />
|
|
202
|
+
<ErrorMessage name="email" component="div" className="text-danger" />
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div className="mb-3">
|
|
206
|
+
<label htmlFor="comment" className="form-label">Comment</label>
|
|
207
|
+
<Field type="text" id="comment" name="comment" className="form-control" />
|
|
208
|
+
<ErrorMessage name="comment" component="div" className="text-danger" />
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div className="mb-3">
|
|
212
|
+
<Field type='checkbox' id="checkbox" name="checkbox" className="form-check-input me-2" />
|
|
213
|
+
<label htmlFor="checkbox" className="form-label">Accept terms of use</label>
|
|
214
|
+
<ErrorMessage name="checkbox" component="div" className="text-danger" />
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<button type="submit" className="btn btn-primary" disabled={isSubmitting}>
|
|
218
|
+
{isSubmitting ? 'Sending...' : 'Vote'}
|
|
219
|
+
</button>
|
|
220
|
+
</Form>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
</Formik>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export default Felvisz
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
App.jsx
|
|
231
|
+
import { useEffect, useState } from "react";
|
|
232
|
+
import { BrowserRouter, Routes, Route } from "react-router";
|
|
233
|
+
import Home from "./pages/Home";
|
|
234
|
+
import Felvisz from "./pages/Felvisz";
|
|
235
|
+
|
|
236
|
+
function App() {
|
|
237
|
+
const [gyartok, setGyartok] = useState([])
|
|
238
|
+
const [autok, setAutok] = useState([])
|
|
239
|
+
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
fetchGyartok();
|
|
242
|
+
fetchAutok();
|
|
243
|
+
}, []);
|
|
244
|
+
|
|
245
|
+
const fetchGyartok = async () => {
|
|
246
|
+
const res = await fetch('https://caroftheyear2023.jedlik.cloud/api/manufacturers')
|
|
247
|
+
const data = await res.json()
|
|
248
|
+
setGyartok(data)
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const fetchAutok = async () => {
|
|
252
|
+
const res = await fetch('https://caroftheyear2023.jedlik.cloud/api/cars')
|
|
253
|
+
const data = await res.json()
|
|
254
|
+
setAutok(data)
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<>
|
|
259
|
+
<BrowserRouter>
|
|
260
|
+
<Routes>
|
|
261
|
+
<Route path='/' element={<Home gyartok={gyartok} />} />
|
|
262
|
+
<Route path="/voting/:id" element={<Felvisz autok={autok}/>}/>
|
|
263
|
+
<Route path="/voting" element={<Felvisz autok={autok}/>}/>
|
|
264
|
+
</Routes>
|
|
265
|
+
</BrowserRouter>
|
|
266
|
+
</>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export default App
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
////////////////LAYOUT (BOOKS)////////////////
|
|
274
|
+
components/Layout.jsx
|
|
275
|
+
import { Outlet } from "react-router";
|
|
276
|
+
import Navigation from "./Navigation";
|
|
277
|
+
|
|
278
|
+
function Layout() {
|
|
279
|
+
return (
|
|
280
|
+
<div className="layout">
|
|
281
|
+
<header>
|
|
282
|
+
<Navigation />
|
|
283
|
+
</header>
|
|
284
|
+
<main className="main-content">
|
|
285
|
+
<Outlet />
|
|
286
|
+
</main>
|
|
287
|
+
</div>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export default Layout;
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
components/Navigation.jsx
|
|
295
|
+
import { NavLink } from "react-router";
|
|
296
|
+
|
|
297
|
+
const Navigation = () => {
|
|
298
|
+
return (
|
|
299
|
+
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
|
300
|
+
<div class="container-fluid">
|
|
301
|
+
<a class="navbar-brand" href="#">Books</a>
|
|
302
|
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
303
|
+
<span class="navbar-toggler-icon"></span>
|
|
304
|
+
</button>
|
|
305
|
+
<div class="collapse navbar-collapse" id="navbarNav">
|
|
306
|
+
<ul class="navbar-nav">
|
|
307
|
+
<li class="nav-item">
|
|
308
|
+
<NavLink className="nav-link active" aria-current="page" to="/">Lista nézet</NavLink>
|
|
309
|
+
</li>
|
|
310
|
+
<li class="nav-item">
|
|
311
|
+
<NavLink className="nav-link" to="/booktable">Táblázatos nézet</NavLink>
|
|
312
|
+
</li>
|
|
313
|
+
</ul>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</nav>
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export default Navigation
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
App.jsx
|
|
324
|
+
import books from "./bookdata"
|
|
325
|
+
import BookList from "./pages/BookList"
|
|
326
|
+
import BookTable from "./pages/BookTable"
|
|
327
|
+
import BookDetails from "./pages/BookDetails";
|
|
328
|
+
import { createBrowserRouter, RouterProvider } from "react-router";
|
|
329
|
+
import Layout from "./components/Layout";
|
|
330
|
+
|
|
331
|
+
const router = createBrowserRouter([
|
|
332
|
+
{
|
|
333
|
+
path: "/",
|
|
334
|
+
element: <Layout />,
|
|
335
|
+
children: [
|
|
336
|
+
{ index: true, element: <BookList bookList={books}/> },
|
|
337
|
+
{ path: "booktable", element: <BookTable bookList={books}/> },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
path: "/books/:id",
|
|
342
|
+
element: <Layout />,
|
|
343
|
+
children: [
|
|
344
|
+
{ index: true, element: <BookDetails books={books} /> },
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
path: "*",
|
|
349
|
+
element: (
|
|
350
|
+
<div style={{ padding: "2rem", textAlign: "center" }}>
|
|
351
|
+
<h1>404 - Az oldal nem található</h1>
|
|
352
|
+
<a href="/">Vissza a főoldalra</a>
|
|
353
|
+
</div>
|
|
354
|
+
),
|
|
355
|
+
},
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
function App() {
|
|
359
|
+
return <RouterProvider router={router} />;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export default App
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import react from 'eslint-plugin-react'
|
|
4
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
5
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
6
|
+
|
|
7
|
+
export default [
|
|
8
|
+
{ ignores: ['dist'] },
|
|
9
|
+
{
|
|
10
|
+
files: ['**/*.{js,jsx}'],
|
|
11
|
+
languageOptions: {
|
|
12
|
+
ecmaVersion: 2020,
|
|
13
|
+
globals: globals.browser,
|
|
14
|
+
parserOptions: {
|
|
15
|
+
ecmaVersion: 'latest',
|
|
16
|
+
ecmaFeatures: { jsx: true },
|
|
17
|
+
sourceType: 'module',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
settings: { react: { version: '18.3' } },
|
|
21
|
+
plugins: {
|
|
22
|
+
react,
|
|
23
|
+
'react-hooks': reactHooks,
|
|
24
|
+
'react-refresh': reactRefresh,
|
|
25
|
+
},
|
|
26
|
+
rules: {
|
|
27
|
+
...js.configs.recommended.rules,
|
|
28
|
+
...react.configs.recommended.rules,
|
|
29
|
+
...react.configs['jsx-runtime'].rules,
|
|
30
|
+
...reactHooks.configs.recommended.rules,
|
|
31
|
+
'react/jsx-no-target-blank': 'off',
|
|
32
|
+
'react/prop-types': 'off',
|
|
33
|
+
'react-refresh/only-export-components': [
|
|
34
|
+
'warn',
|
|
35
|
+
{ allowConstantExport: true },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Vite + React</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|