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 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,7 @@
1
+ # Mivel ez egy Vite projekt, az environment variableokat VITE_ prefixszel kell kezdeni,
2
+ # hogy azok elérhetőek legyenek!
3
+ VITE_API_BASE_URL=http://localhost:3000/api
4
+
5
+ # API végpontok
6
+ VITE_API_EGY_OLDAL=/gyartok
7
+ VITE_API_TOBB_OLDAL=/telefonok
@@ -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>