himanshu-toolkit 0.0.0-stage → 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.
Files changed (4) hide show
  1. package/App.jsx +272 -0
  2. package/package.json +6 -3
  3. package/server.js +154 -0
  4. package/README.md +0 -3
package/App.jsx ADDED
@@ -0,0 +1,272 @@
1
+ import { useState, useEffect } from 'react';
2
+ import 'bootstrap/dist/css/bootstrap.min.css';
3
+
4
+ const API = 'http://localhost:5000/api/cars';
5
+ const currentYear = new Date().getFullYear();
6
+ const emptyCar = {
7
+ ownerName: '', carBrand: '', carModel: '', fuelType: '',
8
+ manufacturingYear: '', sellingPrice: '', kilometersDriven: '',
9
+ contactNumber: '', negotiable: false
10
+ };
11
+ const carModels = {
12
+ 'Maruti Suzuki': ['Swift', 'Baleno', 'Brezza', 'Dzire', 'Wagon R', 'Alto'],
13
+ Hyundai: ['i20', 'Creta', 'Venue', 'Verna', 'Grand i10 Nios'],
14
+ Tata: ['Nexon', 'Punch', 'Tiago', 'Altroz', 'Harrier'],
15
+ Honda: ['City', 'Amaze', 'Jazz', 'Elevate', 'WR-V']
16
+ };
17
+ const fuels = ['Petrol', 'Diesel', 'CNG', 'Electric'];
18
+ const get = (item, camel, snake) => item[camel] ?? item[snake] ?? '';
19
+
20
+ function Home() {
21
+ const [car, setCar] = useState({ ...emptyCar });
22
+ const [cars, setCars] = useState([]);
23
+ const [editId, setEditId] = useState(null);
24
+ const [success, setSuccess] = useState('');
25
+ const [error, setError] = useState('');
26
+
27
+ const fetchCars = async () => {
28
+ try {
29
+ const res = await fetch(API);
30
+ if (!res.ok) throw new Error();
31
+ setCars(await res.json());
32
+ } catch (err) {
33
+ console.error('Fetch error:', err);
34
+ setError('Could not load cars. Check your backend server.');
35
+ }
36
+ };
37
+ useEffect(() => { fetchCars(); }, []);
38
+
39
+ const onTextChange = ({ target: { name, value, type, checked } }) => {
40
+ setCar(prev => ({
41
+ ...prev, [name]: type === 'checkbox' ? checked : value,
42
+ ...(name === 'carBrand' ? { carModel: '' } : {})
43
+ }));
44
+ setSuccess('');
45
+ setError('');
46
+ };
47
+
48
+ const validateForm = () => {
49
+ if (!car.ownerName.trim() || !car.carBrand || !car.carModel || !car.fuelType ||
50
+ !car.manufacturingYear || car.sellingPrice === '' ||
51
+ car.kilometersDriven === '' || !car.contactNumber.trim())
52
+ return 'Please fill in all required fields.';
53
+ if (car.ownerName.trim().length > 25) return 'Owner name cannot exceed 25 characters.';
54
+ if (!carModels[car.carBrand]?.includes(car.carModel)) return 'Please select a valid car model.';
55
+ if (!fuels.includes(car.fuelType)) return 'Please select a valid fuel type.';
56
+
57
+ const year = Number(car.manufacturingYear), price = Number(car.sellingPrice),
58
+ km = Number(car.kilometersDriven);
59
+ if (!Number.isInteger(year) || year < 1886 || year > currentYear)
60
+ return `Enter a valid manufacturing year between 1886 and ${currentYear}.`;
61
+ if (!Number.isFinite(price) || price <= 0) return 'Selling price must be greater than zero.';
62
+ if (!Number.isFinite(km) || km < 0) return 'Kilometers driven must be zero or greater.';
63
+ if (!/^[0-9]{10}$/.test(car.contactNumber.trim()))
64
+ return 'Contact number must contain exactly 10 digits.';
65
+ return '';
66
+ };
67
+
68
+ const resetForm = () => { setCar({ ...emptyCar }); setEditId(null); };
69
+ const handleSaveRecord = async e => {
70
+ e.preventDefault();
71
+ setSuccess(''); setError('');
72
+ const validationError = validateForm();
73
+ if (validationError) return setError(validationError);
74
+
75
+ const payload = {
76
+ ...car, ownerName: car.ownerName.trim(),
77
+ manufacturingYear: Number(car.manufacturingYear),
78
+ sellingPrice: Number(car.sellingPrice),
79
+ kilometersDriven: Number(car.kilometersDriven),
80
+ contactNumber: car.contactNumber.trim()
81
+ };
82
+ try {
83
+ const res = await fetch(editId !== null ? `${API}/${editId}` : API, {
84
+ method: editId !== null ? 'PUT' : 'POST',
85
+ headers: { 'Content-Type': 'application/json' },
86
+ body: JSON.stringify(payload)
87
+ });
88
+ const result = await res.json();
89
+ if (!res.ok) throw new Error(result.error || 'Failed to save car details.');
90
+ setSuccess(editId !== null ? 'Car details updated successfully!' : 'Car listed for sale successfully!');
91
+ resetForm();
92
+ await fetchCars();
93
+ } catch (err) {
94
+ console.error('Save error:', err);
95
+ setError(err.message);
96
+ }
97
+ };
98
+
99
+ const handleEdit = item => {
100
+ setCar({
101
+ ownerName: get(item, 'ownerName', 'owner_name'),
102
+ carBrand: get(item, 'carBrand', 'car_brand'),
103
+ carModel: get(item, 'carModel', 'car_model'),
104
+ fuelType: get(item, 'fuelType', 'fuel_type'),
105
+ manufacturingYear: String(get(item, 'manufacturingYear', 'manufacturing_year')),
106
+ sellingPrice: String(get(item, 'sellingPrice', 'selling_price')),
107
+ kilometersDriven: String(get(item, 'kilometersDriven', 'kilometers_driven')),
108
+ contactNumber: String(get(item, 'contactNumber', 'contact_number')),
109
+ negotiable: Boolean(item.negotiable)
110
+ });
111
+ setEditId(item.id ?? item.ID);
112
+ setSuccess(''); setError('');
113
+ window.scrollTo({ top: 0, behavior: 'smooth' });
114
+ };
115
+
116
+ const handleDelete = async id => {
117
+ if (!window.confirm('Are you sure you want to delete this car listing?')) return;
118
+ setSuccess(''); setError('');
119
+ try {
120
+ const res = await fetch(`${API}/${id}`, { method: 'DELETE' });
121
+ const result = await res.json();
122
+ if (!res.ok) throw new Error(result.error || 'Failed to delete car listing.');
123
+ if (String(editId) === String(id)) resetForm();
124
+ setSuccess('Car listing deleted successfully!');
125
+ await fetchCars();
126
+ } catch (err) {
127
+ console.error('Delete error:', err);
128
+ setError(err.message);
129
+ }
130
+ };
131
+
132
+ const handleCancel = () => {
133
+ resetForm();
134
+ setSuccess(''); setError('');
135
+ };
136
+
137
+ return (
138
+ <div className="container mt-5 mb-5">
139
+ <h2 className="text-center mb-2">Used Car Selling Management System</h2>
140
+ <p className="text-center text-muted mb-4">List your used car for sale</p>
141
+
142
+ {success && <div className="alert alert-success" role="status">{success}</div>}
143
+ {error && <div className="alert alert-danger" role="alert">{error}</div>}
144
+
145
+ <div className="card shadow p-4 mb-5">
146
+ <h4 className="mb-3">{editId !== null ? 'Edit Car Listing' : 'Sell Your Car'}</h4>
147
+ <form onSubmit={handleSaveRecord}>
148
+ <div className="row">
149
+ <div className="col-md-6 mb-3">
150
+ <label className="form-label">Owner Name</label>
151
+ <input type="text" name="ownerName" className="form-control"
152
+ value={car.ownerName} onChange={onTextChange} maxLength={25}
153
+ placeholder="Enter owner name" required />
154
+ <small className="text-muted">Maximum 25 characters</small>
155
+ </div>
156
+
157
+ <div className="col-md-6 mb-3">
158
+ <label className="form-label">Car Brand</label>
159
+ <select name="carBrand" className="form-select" value={car.carBrand}
160
+ onChange={onTextChange} required>
161
+ <option value="">Select car brand</option>
162
+ {Object.keys(carModels).map(brand => <option key={brand} value={brand}>{brand}</option>)}
163
+ </select>
164
+ </div>
165
+
166
+ <div className="col-md-6 mb-3">
167
+ <label className="form-label">Car Model</label>
168
+ <select name="carModel" className="form-select" value={car.carModel}
169
+ onChange={onTextChange} disabled={!car.carBrand} required>
170
+ <option value="">Select car model</option>
171
+ {(carModels[car.carBrand] || []).map(model => <option key={model} value={model}>{model}</option>)}
172
+ </select>
173
+ </div>
174
+
175
+ <div className="col-md-6 mb-3">
176
+ <label className="form-label">Fuel Type</label>
177
+ <select name="fuelType" className="form-select" value={car.fuelType}
178
+ onChange={onTextChange} required>
179
+ <option value="">Select fuel type</option>
180
+ {fuels.map(fuel => <option key={fuel} value={fuel}>{fuel}</option>)}
181
+ </select>
182
+ </div>
183
+
184
+ <div className="col-md-6 mb-3">
185
+ <label className="form-label">Manufacturing Year</label>
186
+ <input type="number" name="manufacturingYear" className="form-control"
187
+ value={car.manufacturingYear} onChange={onTextChange} min="1886"
188
+ max={currentYear} step="1" placeholder="e.g. 2020" required />
189
+ </div>
190
+
191
+ <div className="col-md-6 mb-3">
192
+ <label className="form-label">Selling Price (₹)</label>
193
+ <input type="number" name="sellingPrice" className="form-control"
194
+ value={car.sellingPrice} onChange={onTextChange} min="0.01"
195
+ step="0.01" placeholder="Enter selling price" required />
196
+ </div>
197
+
198
+ <div className="col-md-6 mb-3">
199
+ <label className="form-label">Kilometers Driven</label>
200
+ <input type="number" name="kilometersDriven" className="form-control"
201
+ value={car.kilometersDriven} onChange={onTextChange} min="0"
202
+ step="1" placeholder="e.g. 45000" required />
203
+ </div>
204
+
205
+ <div className="col-md-6 mb-3">
206
+ <label className="form-label">Contact Number</label>
207
+ <input type="tel" name="contactNumber" className="form-control"
208
+ value={car.contactNumber} onChange={onTextChange} maxLength={10}
209
+ pattern="[0-9]{10}" inputMode="numeric"
210
+ placeholder="Enter 10-digit number" required />
211
+ </div>
212
+
213
+ <div className="col-12 mb-3">
214
+ <div className="form-check">
215
+ <input type="checkbox" name="negotiable" className="form-check-input"
216
+ id="negotiable" checked={car.negotiable} onChange={onTextChange} />
217
+ <label className="form-check-label" htmlFor="negotiable">Price is negotiable</label>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <button type="submit" className="btn btn-primary">
223
+ {editId !== null ? 'Update Car' : 'List Car for Sale'}
224
+ </button>
225
+ {editId !== null && <button type="button" className="btn btn-secondary ms-2" onClick={handleCancel}>Cancel</button>}
226
+ </form>
227
+ </div>
228
+
229
+ <h4 className="mb-3">Used Car Listings</h4>
230
+ <div className="table-responsive">
231
+ <table className="table table-bordered table-striped table-hover align-middle">
232
+ <thead className="table-dark">
233
+ <tr>
234
+ <th>ID</th><th>Owner</th><th>Brand</th><th>Model</th><th>Fuel</th>
235
+ <th>Year</th><th>Price (₹)</th><th>Kilometers</th><th>Contact</th>
236
+ <th>Negotiable</th><th>Actions</th>
237
+ </tr>
238
+ </thead>
239
+ <tbody>
240
+ {!cars.length ? (
241
+ <tr><td colSpan="11" className="text-center">No car listings found</td></tr>
242
+ ) : cars.map(item => {
243
+ const id = item.id ?? item.ID;
244
+ return (
245
+ <tr key={id}>
246
+ <td>{id}</td>
247
+ <td>{get(item, 'ownerName', 'owner_name')}</td>
248
+ <td>{get(item, 'carBrand', 'car_brand')}</td>
249
+ <td>{get(item, 'carModel', 'car_model')}</td>
250
+ <td>{get(item, 'fuelType', 'fuel_type')}</td>
251
+ <td>{get(item, 'manufacturingYear', 'manufacturing_year')}</td>
252
+ <td>₹{Number(get(item, 'sellingPrice', 'selling_price')).toLocaleString('en-IN')}</td>
253
+ <td>{Number(get(item, 'kilometersDriven', 'kilometers_driven')).toLocaleString('en-IN')} km</td>
254
+ <td>{get(item, 'contactNumber', 'contact_number')}</td>
255
+ <td>{[true, 1, '1'].includes(item.negotiable) ? 'Yes' : 'No'}</td>
256
+ <td>
257
+ <button type="button" className="btn btn-warning btn-sm me-2 mb-1"
258
+ onClick={() => handleEdit(item)}>Edit</button>
259
+ <button type="button" className="btn btn-danger btn-sm mb-1"
260
+ onClick={() => handleDelete(id)}>Delete</button>
261
+ </td>
262
+ </tr>
263
+ );
264
+ })}
265
+ </tbody>
266
+ </table>
267
+ </div>
268
+ </div>
269
+ );
270
+ }
271
+
272
+ export default Home;
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "himanshu-toolkit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "description": "My own reusable JavaScript package",
5
+ "main": "index.js",
6
+ "keywords": ["javascript", "utility"],
7
+ "author": "Himanshu",
8
+ "license": "MIT"
6
9
  }
package/server.js ADDED
@@ -0,0 +1,154 @@
1
+
2
+ const express = require('express');
3
+ const mysql = require('mysql2');
4
+ const cors = require('cors');
5
+
6
+ const app = express();
7
+ app.use(cors(), express.json());
8
+
9
+ const db = mysql.createConnection({
10
+ host: 'localhost',
11
+ user: 'root',
12
+ password: process.env.DB_PASSWORD || 'Himanshu@0334',
13
+ database: 'used_car_db'
14
+ });
15
+
16
+ db.connect(err => {
17
+ if (err) return console.error('MySQL connection error:', err.message);
18
+ console.log('Connected to Used Car Database!');
19
+ });
20
+
21
+ const carModels = {
22
+ 'Maruti Suzuki': ['Swift', 'Baleno', 'Brezza', 'Dzire', 'Wagon R', 'Alto'],
23
+ Hyundai: ['i20', 'Creta', 'Venue', 'Verna', 'Grand i10 Nios'],
24
+ Tata: ['Nexon', 'Punch', 'Tiago', 'Altroz', 'Harrier'],
25
+ Honda: ['City', 'Amaze', 'Jazz', 'Elevate', 'WR-V']
26
+ };
27
+
28
+ const fuelTypes = ['Petrol', 'Diesel', 'CNG', 'Electric'];
29
+
30
+ function validateCar(c) {
31
+ const year = Number(c.manufacturingYear);
32
+ const price = Number(c.sellingPrice);
33
+ const km = Number(c.kilometersDriven);
34
+ const currentYear = new Date().getFullYear();
35
+
36
+ if (typeof c.ownerName !== 'string' || !c.ownerName.trim() || c.ownerName.trim().length > 25)
37
+ return 'Owner name is required and cannot exceed 25 characters.';
38
+ if (!Object.prototype.hasOwnProperty.call(carModels, c.carBrand))
39
+ return 'Please select a valid car brand.';
40
+ if (!carModels[c.carBrand].includes(c.carModel))
41
+ return 'Please select a valid model for the selected brand.';
42
+ if (!fuelTypes.includes(c.fuelType))
43
+ return 'Please select a valid fuel type.';
44
+ if (c.manufacturingYear === '' || c.manufacturingYear == null ||
45
+ !Number.isInteger(year) || year < 1886 || year > currentYear)
46
+ return `Manufacturing year must be between 1886 and ${currentYear}.`;
47
+ if (c.sellingPrice === '' || c.sellingPrice == null ||
48
+ !Number.isFinite(price) || price <= 0)
49
+ return 'Selling price must be greater than zero.';
50
+ if (c.kilometersDriven === '' || c.kilometersDriven == null ||
51
+ !Number.isSafeInteger(km) || km < 0)
52
+ return 'Kilometers driven must be a non-negative whole number.';
53
+ if (typeof c.contactNumber !== 'string' || !/^[0-9]{10}$/.test(c.contactNumber))
54
+ return 'Contact number must contain exactly 10 digits.';
55
+ if (c.negotiable !== undefined && typeof c.negotiable !== 'boolean')
56
+ return 'Negotiable must be true or false.';
57
+
58
+ return null;
59
+ }
60
+
61
+ const columns = [
62
+ 'ownerName', 'carBrand', 'carModel', 'fuelType',
63
+ 'manufacturingYear', 'sellingPrice', 'kilometersDriven',
64
+ 'contactNumber', 'negotiable'
65
+ ];
66
+
67
+ const getValues = c => [
68
+ c.ownerName.trim(), c.carBrand, c.carModel, c.fuelType,
69
+ Number(c.manufacturingYear), Number(c.sellingPrice),
70
+ Number(c.kilometersDriven), c.contactNumber, c.negotiable ?? false
71
+ ];
72
+
73
+ const fail = (res, status, error) => res.status(status).json({ error });
74
+
75
+ app.get('/api/cars', (req, res) => {
76
+ db.query('SELECT * FROM cars ORDER BY id DESC', (err, rows) => {
77
+ if (err) {
78
+ console.error('Fetch error:', err.message);
79
+ return fail(res, 500, 'Failed to fetch car listings.');
80
+ }
81
+ res.json(rows);
82
+ });
83
+ });
84
+
85
+ app.post('/api/cars', (req, res) => {
86
+ const error = validateCar(req.body);
87
+ if (error) return fail(res, 400, error);
88
+
89
+ const sql = `INSERT INTO cars (${columns.join(', ')})
90
+ VALUES (${columns.map(() => '?').join(', ')})`;
91
+
92
+ db.query(sql, getValues(req.body), (err, result) => {
93
+ if (err) {
94
+ console.error('Insert error:', err.message);
95
+ return fail(res, 500, 'Failed to add car listing.');
96
+ }
97
+ res.status(201).json({
98
+ message: 'Car listed for sale successfully!',
99
+ id: result.insertId
100
+ });
101
+ });
102
+ });
103
+
104
+ app.put('/api/cars/:id', (req, res) => {
105
+ const id = Number(req.params.id);
106
+ if (!Number.isSafeInteger(id) || id <= 0)
107
+ return fail(res, 400, 'Invalid car ID.');
108
+
109
+ const error = validateCar(req.body);
110
+ if (error) return fail(res, 400, error);
111
+
112
+ const sql = `UPDATE cars SET ${columns.map(c => `${c} = ?`).join(', ')} WHERE id = ?`;
113
+
114
+ db.query(sql, [...getValues(req.body), id], (err, result) => {
115
+ if (err) {
116
+ console.error('Update error:', err.message);
117
+ return fail(res, 500, 'Failed to update car listing.');
118
+ }
119
+
120
+ if (result.affectedRows)
121
+ return res.json({ message: 'Car details updated successfully!' });
122
+
123
+ db.query('SELECT id FROM cars WHERE id = ?', [id], (err, rows) => {
124
+ if (err) return fail(res, 500, 'Failed to verify car listing.');
125
+ if (!rows.length) return fail(res, 404, 'Car listing not found.');
126
+ res.json({ message: 'Car details are already up to date.' });
127
+ });
128
+ });
129
+ });
130
+
131
+ app.delete('/api/cars/:id', (req, res) => {
132
+ const id = Number(req.params.id);
133
+ if (!Number.isSafeInteger(id) || id <= 0)
134
+ return fail(res, 400, 'Invalid car ID.');
135
+
136
+ db.query('DELETE FROM cars WHERE id = ?', [id], (err, result) => {
137
+ if (err) {
138
+ console.error('Delete error:', err.message);
139
+ return fail(res, 500, 'Failed to delete car listing.');
140
+ }
141
+ if (!result.affectedRows)
142
+ return fail(res, 404, 'Car listing not found.');
143
+
144
+ res.json({ message: 'Car listing deleted successfully!' });
145
+ });
146
+ });
147
+
148
+ app.use((err, req, res, next) => {
149
+ if (err instanceof SyntaxError && err.status === 400)
150
+ return fail(res, 400, 'Invalid JSON request body.');
151
+ next(err);
152
+ });
153
+
154
+ app.listen(5000, () => console.log('Server running at http://localhost:5000'));
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.