himanshu-toolkit 1.0.2 → 1.0.4

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/App2.jsx ADDED
@@ -0,0 +1,244 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import 'bootstrap/dist/css/bootstrap.min.css';
3
+
4
+ const API = 'http://localhost:5000/api/laptops';
5
+ const emptyLaptop = {
6
+ sellerName: '', laptopBrand: '', laptopModel: '', processorType: '',
7
+ laptopRam: '', storageType: '', laptopColor: '', sellingPrice: '',
8
+ contactNumber: '', Negotiable: false
9
+ };
10
+ const laptopOptions = {
11
+ HP: ['Pavilion', 'Omen', 'EliteBook', 'G16'],
12
+ Dell: ['Inspiron', 'Latitude', 'XPS', 'Gaming'],
13
+ Lenovo: ['LOQ', 'Legion', 'ThinkPad', 'IdeaPad'],
14
+ Apple: ['MacBook Air M1', 'MacBook Air M2', 'MacBook Pro', 'MacBook Neo']
15
+ };
16
+ const processorOptions = {
17
+ HP: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7'],
18
+ Dell: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7'],
19
+ Lenovo: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7'],
20
+ Apple: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7']
21
+ };
22
+ const ramOptions = ['4 GB', '8 GB', '16 GB', '32 GB'];
23
+ const storageOptions = ['HDD', 'SSD'];
24
+ const get = (item, camel, snake) => item[camel] ?? item[snake] ?? '';
25
+
26
+ export default function App() {
27
+ const [laptop, setLaptop] = useState({ ...emptyLaptop });
28
+ const [laptops, setLaptops] = useState([]);
29
+ const [editId, setEditId] = useState(null);
30
+ const [success, setSuccess] = useState('');
31
+ const [error, setError] = useState('');
32
+
33
+ const fetchLaptops = async () => {
34
+ try {
35
+ const res = await fetch(API);
36
+ if (!res.ok) throw new Error('Failed to fetch laptop records.');
37
+ setLaptops(await res.json());
38
+ } catch (err) {
39
+ console.error('Fetch error:', err);
40
+ setError('Could not load laptops. Check your backend server.');
41
+ }
42
+ };
43
+ useEffect(() => { fetchLaptops(); }, []);
44
+
45
+ const onTextChange = ({ target: { name, value, type, checked } }) => {
46
+ setLaptop(prev => ({
47
+ ...prev,
48
+ [name]: type === 'checkbox' ? checked : value,
49
+ ...(name === 'laptopBrand' ? { laptopModel: '', processorType: '' } : {})
50
+ }));
51
+ setSuccess('');
52
+ setError('');
53
+ };
54
+
55
+ const validateForm = () => {
56
+ if (!laptop.sellerName.trim() || !laptop.laptopBrand || !laptop.laptopModel ||
57
+ !laptop.processorType || !laptop.laptopRam || !laptop.storageType ||
58
+ !laptop.laptopColor.trim() || laptop.sellingPrice === '' || !laptop.contactNumber)
59
+ return 'Please fill in all required fields.';
60
+ if (laptop.sellerName.trim().length > 20)
61
+ return 'Seller name cannot exceed 20 characters.';
62
+ if (!laptopOptions[laptop.laptopBrand]?.includes(laptop.laptopModel))
63
+ return 'Please select a valid laptop model for the selected brand.';
64
+ if (!processorOptions[laptop.laptopBrand]?.includes(laptop.processorType))
65
+ return 'Please select a valid processor type for the selected brand.';
66
+ if (!ramOptions.includes(laptop.laptopRam))
67
+ return 'RAM must be 4 GB, 8 GB, 16 GB, or 32 GB.';
68
+ if (!storageOptions.includes(laptop.storageType))
69
+ return 'Please select HDD or SSD.';
70
+ if (!Number.isFinite(Number(laptop.sellingPrice)) || Number(laptop.sellingPrice) <= 0)
71
+ return 'Selling price must be a positive number.';
72
+ if (!/^[0-9]{10}$/.test(laptop.contactNumber.trim()))
73
+ return 'Contact number must contain exactly 10 digits.';
74
+ return '';
75
+ };
76
+
77
+ const resetForm = () => {
78
+ setLaptop({ ...emptyLaptop });
79
+ setEditId(null);
80
+ };
81
+
82
+ const handleSaveRecord = async e => {
83
+ e.preventDefault();
84
+ setSuccess('');
85
+ setError('');
86
+ const validationError = validateForm();
87
+ if (validationError) return setError(validationError);
88
+
89
+ const payload = {
90
+ ...laptop,
91
+ sellerName: laptop.sellerName.trim(),
92
+ laptopModel: laptop.laptopModel.trim(),
93
+ laptopColor: laptop.laptopColor.trim(),
94
+ sellingPrice: Number(laptop.sellingPrice)
95
+ };
96
+ try {
97
+ const res = await fetch(editId !== null ? `${API}/${editId}` : API, {
98
+ method: editId !== null ? 'PUT' : 'POST',
99
+ headers: { 'Content-Type': 'application/json' },
100
+ body: JSON.stringify(payload)
101
+ });
102
+ const result = await res.json();
103
+ if (!res.ok) throw new Error(result.error || 'Failed to save laptop details.');
104
+ setSuccess(editId !== null ? 'Laptop details updated successfully!' : 'Laptop listed successfully!');
105
+ resetForm();
106
+ await fetchLaptops();
107
+ } catch (err) {
108
+ console.error('Save error:', err);
109
+ setError(err.message || 'Failed to save laptop details.');
110
+ }
111
+ };
112
+
113
+ const handleEdit = item => {
114
+ setLaptop({
115
+ sellerName: get(item, 'sellerName', 'seller_name'),
116
+ laptopBrand: get(item, 'laptopBrand', 'laptop_brand'),
117
+ laptopModel: get(item, 'laptopModel', 'laptop_model'),
118
+ processorType: get(item, 'processorType', 'processor_type'),
119
+ laptopRam: get(item, 'laptopRam', 'laptop_ram'),
120
+ storageType: get(item, 'storageType', 'storage_type'),
121
+ laptopColor: get(item, 'laptopColor', 'laptop_color'),
122
+ sellingPrice: String(get(item, 'sellingPrice', 'selling_price')),
123
+ contactNumber: String(get(item, 'contactNumber', 'contact_number')),
124
+ Negotiable: Boolean(get(item, 'Negotiable', 'negotiable'))
125
+ });
126
+ setEditId(item.id ?? item.ID);
127
+ setSuccess(''); setError('');
128
+ window.scrollTo({ top: 0, behavior: 'smooth' });
129
+ };
130
+
131
+ const handleDelete = async id => {
132
+ if (!window.confirm('Are you sure you want to delete this laptop listing?')) return;
133
+ setSuccess(''); setError('');
134
+ try {
135
+ const res = await fetch(`${API}/${id}`, { method: 'DELETE' });
136
+ const result = await res.json();
137
+ if (!res.ok) throw new Error(result.error || 'Failed to delete laptop listing.');
138
+ if (String(editId) === String(id)) resetForm();
139
+ setSuccess('Laptop listing deleted successfully!');
140
+ await fetchLaptops();
141
+ } catch (err) {
142
+ console.error('Delete error:', err);
143
+ setError(err.message || 'Failed to delete laptop listing.');
144
+ }
145
+ };
146
+
147
+ const handleCancel = () => { resetForm(); setSuccess(''); setError(''); };
148
+ const fieldError = message => message && <small className="text-danger d-block">{message}</small>;
149
+
150
+ return (
151
+ <div className="container mt-5 mb-5">
152
+ <h2 className="text-center mb-2">Laptop Selling Management</h2>
153
+ <p className="text-center text-muted mb-4">Sell your laptop</p>
154
+ {success && <div className="alert alert-success" role="status">{success}</div>}
155
+ {error && <div className="alert alert-danger" role="alert">{error}</div>}
156
+
157
+ <div className="card shadow p-4 mb-5">
158
+ <h4 className="mb-3">{editId !== null ? 'Edit Laptop Listing' : 'List Your Laptop'}</h4>
159
+ <form onSubmit={handleSaveRecord}>
160
+ <div className="row">
161
+ <div className="col-md-6 mb-3">
162
+ <label className="form-label">Seller Name</label>
163
+ <input type="text" name="sellerName" className="form-control" value={laptop.sellerName} onChange={onTextChange} maxLength={20} placeholder="Enter seller name" required />
164
+ <small className="text-muted">Maximum 20 characters</small>
165
+ </div>
166
+ <div className="col-md-6 mb-3">
167
+ <label className="form-label">Laptop Brand</label>
168
+ <select name="laptopBrand" className="form-select" value={laptop.laptopBrand} onChange={onTextChange} required>
169
+ <option value="">Select laptop brand</option>
170
+ {Object.keys(laptopOptions).map(brand => <option key={brand} value={brand}>{brand}</option>)}
171
+ </select>
172
+ </div>
173
+ <div className="col-md-6 mb-3">
174
+ <label className="form-label">Laptop Model</label>
175
+ <select name="laptopModel" className="form-select" value={laptop.laptopModel} onChange={onTextChange} disabled={!laptop.laptopBrand} required>
176
+ <option value="">Select laptop model</option>
177
+ {(laptopOptions[laptop.laptopBrand] || []).map(model => <option key={model} value={model}>{model}</option>)}
178
+ </select>
179
+ </div>
180
+ <div className="col-md-6 mb-3">
181
+ <label className="form-label">Processor Type</label>
182
+ <select name="processorType" className="form-select" value={laptop.processorType} onChange={onTextChange} disabled={!laptop.laptopBrand} required>
183
+ <option value="">Select processor</option>
184
+ {(processorOptions[laptop.laptopBrand] || []).map(p => <option key={p} value={p}>{p}</option>)}
185
+ </select>
186
+ </div>
187
+ <div className="col-md-6 mb-3">
188
+ <label className="form-label">RAM Capacity</label>
189
+ <select name="laptopRam" className="form-select" value={laptop.laptopRam} onChange={onTextChange} required>
190
+ <option value="">Select RAM capacity</option>
191
+ {ramOptions.map(ram => <option key={ram} value={ram}>{ram}</option>)}
192
+ </select>
193
+ </div>
194
+ <div className="col-md-6 mb-3">
195
+ <label className="form-label">Storage Type</label>
196
+ <select name="storageType" className="form-select" value={laptop.storageType} onChange={onTextChange} required>
197
+ <option value="">Select storage type</option>
198
+ {storageOptions.map(type => <option key={type} value={type}>{type}</option>)}
199
+ </select>
200
+ </div>
201
+ <div className="col-md-6 mb-3">
202
+ <label className="form-label">Laptop Color</label>
203
+ <input type="text" name="laptopColor" className="form-control" value={laptop.laptopColor} onChange={onTextChange} placeholder="Enter laptop color" required />
204
+ </div>
205
+ <div className="col-md-6 mb-3">
206
+ <label className="form-label">Selling Price (₹)</label>
207
+ <input type="number" name="sellingPrice" className="form-control" value={laptop.sellingPrice} onChange={onTextChange} min="0.01" step="any" placeholder="Enter selling price" required />
208
+ </div>
209
+ <div className="col-md-6 mb-3">
210
+ <label className="form-label">Contact Number</label>
211
+ <input type="tel" inputMode="numeric" name="contactNumber" className="form-control" value={laptop.contactNumber} onChange={onTextChange} maxLength={10} pattern="[0-9]{10}" placeholder="Enter 10-digit contact number" required />
212
+ </div>
213
+ <div className="col-md-6 mb-3 d-flex align-items-center">
214
+ <div className="form-check mt-3">
215
+ <input type="checkbox" name="Negotiable" className="form-check-input" id="Negotiable" checked={laptop.Negotiable} onChange={onTextChange} />
216
+ <label className="form-check-label" htmlFor="Negotiable">Negotiable</label>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ <button type="submit" className="btn btn-primary">{editId !== null ? 'Update Laptop' : 'List Laptop'}</button>
221
+ {editId !== null && <button type="button" className="btn btn-secondary ms-2" onClick={handleCancel}>Cancel</button>}
222
+ </form>
223
+ </div>
224
+
225
+ <h4 className="mb-3">Laptop Listings</h4>
226
+ <div className="table-responsive">
227
+ <table className="table table-bordered table-striped table-hover align-middle">
228
+ <thead className="table-dark"><tr>
229
+ <th>ID</th><th>Seller</th><th>Brand</th><th>Model</th><th>Processor</th><th>RAM</th><th>Storage</th><th>Color</th><th>Price</th><th>Contact</th><th>Negotiable</th><th>Actions</th>
230
+ </tr></thead>
231
+ <tbody>
232
+ {!laptops.length ? <tr><td colSpan="12" className="text-center">No laptop listings found</td></tr> : laptops.map(item => {
233
+ const id = item.id ?? item.ID;
234
+ return <tr key={id}>
235
+ <td>{id}</td><td>{get(item, 'sellerName', 'seller_name')}</td><td>{get(item, 'laptopBrand', 'laptop_brand')}</td><td>{get(item, 'laptopModel', 'laptop_model')}</td><td>{get(item, 'processorType', 'processor_type')}</td><td>{get(item, 'laptopRam', 'laptop_ram')}</td><td>{get(item, 'storageType', 'storage_type')}</td><td>{get(item, 'laptopColor', 'laptop_color')}</td><td>₹{Number(get(item, 'sellingPrice', 'selling_price')).toLocaleString('en-IN')}</td><td>{get(item, 'contactNumber', 'contact_number')}</td><td>{get(item, 'Negotiable', 'negotiable') ? 'Yes' : 'No'}</td>
236
+ <td><button type="button" className="btn btn-warning btn-sm me-2 mb-1" onClick={() => handleEdit(item)}>Edit</button><button type="button" className="btn btn-danger btn-sm mb-1" onClick={() => handleDelete(id)}>Delete</button></td>
237
+ </tr>;
238
+ })}
239
+ </tbody>
240
+ </table>
241
+ </div>
242
+ </div>
243
+ );
244
+ }
package/database2.sql ADDED
@@ -0,0 +1,18 @@
1
+ CREATE DATABASE IF NOT EXISTS laptop_seller;
2
+ USE laptop_seller;
3
+
4
+ CREATE TABLE IF NOT EXISTS laptops (
5
+ id INT AUTO_INCREMENT PRIMARY KEY,
6
+ seller_name VARCHAR(20) NOT NULL,
7
+ laptop_brand ENUM('HP', 'Dell', 'Lenovo', 'Apple') NOT NULL,
8
+ laptop_model VARCHAR(100) NOT NULL,
9
+ processor_type ENUM('i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7') NOT NULL,
10
+ laptop_ram ENUM('4 GB', '8 GB', '16 GB', '32 GB') NOT NULL,
11
+ storage_type ENUM('HDD', 'SSD') NOT NULL,
12
+ laptop_color VARCHAR(30) NOT NULL,
13
+ selling_price DECIMAL(12,2) NOT NULL,
14
+ contact_number CHAR(10) NOT NULL,
15
+ negotiable BOOLEAN NOT NULL DEFAULT FALSE,
16
+ CHECK (selling_price > 0),
17
+ CHECK (contact_number REGEXP '^[0-9]{10}$')
18
+ );
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "himanshu-toolkit",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "My reusable full-stack application toolkit",
5
5
  "main": "server.js",
6
6
  "files": [
7
7
  "App.jsx",
8
8
  "App1.jsx",
9
+ "App2.jsx",
9
10
  "database.sql",
10
11
  "database1.sql",
12
+ "database2.sql",
11
13
  "server.js",
12
- "server1.js"
14
+ "server1.js",
15
+ "server3.js"
13
16
  ],
14
17
  "keywords": [
15
18
  "react",
@@ -23,4 +26,4 @@
23
26
  "scripts": {
24
27
  "start": "node server.js"
25
28
  }
26
- }
29
+ }
package/server3.js ADDED
@@ -0,0 +1,164 @@
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: 'laptop_seller'
14
+ });
15
+
16
+ db.connect(err => {
17
+ if (err) return console.error('MySQL error:', err.message);
18
+ console.log('Connected to MySQL Database!');
19
+ });
20
+
21
+ const laptopOptions = {
22
+ HP: ['Pavilion', 'Omen', 'EliteBook', 'G16'],
23
+ Dell: ['Inspiron', 'Latitude', 'XPS', 'Gaming'],
24
+ Lenovo: ['LOQ', 'Legion', 'ThinkPad', 'IdeaPad'],
25
+ Apple: ['MacBook Air M1', 'MacBook Air M2', 'MacBook Pro', 'MacBook Neo']
26
+ };
27
+
28
+ const processorOptions = {
29
+ HP: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7'],
30
+ Dell: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7'],
31
+ Lenovo: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7'],
32
+ Apple: ['i3', 'i5', 'i7', 'Ryzen 5', 'Ryzen 7']
33
+ };
34
+
35
+ const ramOptions = ['4 GB', '8 GB', '16 GB', '32 GB'];
36
+ const storageOptions = ['HDD', 'SSD'];
37
+ const fail = (res, status, error) => res.status(status).json({ error });
38
+
39
+ function validateLaptop(l) {
40
+ if (typeof l.sellerName !== 'string' || !l.sellerName.trim())
41
+ return 'Seller name is required.';
42
+ if (l.sellerName.trim().length > 20)
43
+ return 'Seller name cannot exceed 20 characters.';
44
+ if (!laptopOptions[l.laptopBrand])
45
+ return 'Please select a valid laptop brand.';
46
+ if (!laptopOptions[l.laptopBrand].includes(l.laptopModel))
47
+ return 'Please select a valid laptop model.';
48
+ if (!processorOptions[l.laptopBrand].includes(l.processorType))
49
+ return 'Please select a valid processor.';
50
+ if (!ramOptions.includes(l.laptopRam))
51
+ return 'Please select a valid RAM capacity.';
52
+ if (!storageOptions.includes(l.storageType))
53
+ return 'Please select HDD or SSD.';
54
+ if (typeof l.laptopColor !== 'string' || !l.laptopColor.trim())
55
+ return 'Laptop color is required.';
56
+
57
+ const price = Number(l.sellingPrice);
58
+ if (l.sellingPrice === '' || l.sellingPrice == null ||
59
+ !Number.isFinite(price) || price <= 0)
60
+ return 'Selling price must be positive.';
61
+
62
+ if (typeof l.contactNumber !== 'string' ||
63
+ !/^[0-9]{10}$/.test(l.contactNumber))
64
+ return 'Contact number must contain exactly 10 digits.';
65
+
66
+ const negotiable = l.Negotiable ?? l.negotiable;
67
+ if (negotiable !== undefined && typeof negotiable !== 'boolean')
68
+ return 'Negotiable must be true or false.';
69
+
70
+ return null;
71
+ }
72
+
73
+ const getValues = l => [
74
+ l.sellerName.trim(), l.laptopBrand, l.laptopModel,
75
+ l.processorType, l.laptopRam, l.storageType,
76
+ l.laptopColor.trim(), Number(l.sellingPrice),
77
+ l.contactNumber, Boolean(l.Negotiable ?? l.negotiable)
78
+ ];
79
+
80
+ const selectSQL = `SELECT id, seller_name AS sellerName,
81
+ laptop_brand AS laptopBrand, laptop_model AS laptopModel,
82
+ processor_type AS processorType, laptop_ram AS laptopRam,
83
+ storage_type AS storageType, laptop_color AS laptopColor,
84
+ selling_price AS sellingPrice, contact_number AS contactNumber,
85
+ negotiable AS negotiable FROM laptops`;
86
+
87
+ app.get('/api/laptops', (req, res) => {
88
+ db.query(`${selectSQL} ORDER BY id DESC`, (err, rows) => {
89
+ if (err) return fail(res, 500, 'Failed to fetch laptops.');
90
+ res.json(rows);
91
+ });
92
+ });
93
+
94
+ app.post('/api/laptops', (req, res) => {
95
+ const error = validateLaptop(req.body);
96
+ if (error) return fail(res, 400, error);
97
+
98
+ const sql = `INSERT INTO laptops
99
+ (seller_name, laptop_brand, laptop_model, processor_type,
100
+ laptop_ram, storage_type, laptop_color, selling_price,
101
+ contact_number, negotiable)
102
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
103
+
104
+ db.query(sql, getValues(req.body), (err, result) => {
105
+ if (err) {
106
+ console.error(err.message);
107
+ return fail(res, 500, 'Failed to add laptop.');
108
+ }
109
+ res.status(201).json({
110
+ message: 'Laptop listed successfully!',
111
+ id: result.insertId
112
+ });
113
+ });
114
+ });
115
+
116
+ app.put('/api/laptops/:id', (req, res) => {
117
+ const id = Number(req.params.id);
118
+ if (!Number.isSafeInteger(id) || id <= 0)
119
+ return fail(res, 400, 'Invalid laptop ID.');
120
+
121
+ const error = validateLaptop(req.body);
122
+ if (error) return fail(res, 400, error);
123
+
124
+ const sql = `UPDATE laptops SET seller_name=?, laptop_brand=?,
125
+ laptop_model=?, processor_type=?, laptop_ram=?, storage_type=?,
126
+ laptop_color=?, selling_price=?, contact_number=?, negotiable=?
127
+ WHERE id=?`;
128
+
129
+ db.query(sql, [...getValues(req.body), id], (err, result) => {
130
+ if (err) return fail(res, 500, 'Failed to update laptop.');
131
+ if (result.affectedRows)
132
+ return res.json({ message: 'Laptop updated successfully!' });
133
+
134
+ db.query('SELECT id FROM laptops WHERE id=?', [id], (err, rows) => {
135
+ if (err) return fail(res, 500, 'Failed to verify laptop.');
136
+ if (!rows.length) return fail(res, 404, 'Laptop not found.');
137
+ res.json({ message: 'Laptop details are already up to date.' });
138
+ });
139
+ });
140
+ });
141
+
142
+ app.delete('/api/laptops/:id', (req, res) => {
143
+ const id = Number(req.params.id);
144
+ if (!Number.isSafeInteger(id) || id <= 0)
145
+ return fail(res, 400, 'Invalid laptop ID.');
146
+
147
+ db.query('DELETE FROM laptops WHERE id=?', [id], (err, result) => {
148
+ if (err) return fail(res, 500, 'Failed to delete laptop.');
149
+ if (!result.affectedRows)
150
+ return fail(res, 404, 'Laptop not found.');
151
+ res.json({ message: 'Laptop deleted successfully!' });
152
+ });
153
+ });
154
+
155
+ app.use((err, req, res, next) => {
156
+ if (err instanceof SyntaxError && err.status === 400)
157
+ return fail(res, 400, 'Invalid JSON request body.');
158
+ console.error(err);
159
+ res.status(500).json({ error: 'Internal server error.' });
160
+ });
161
+
162
+ app.listen(5000, () =>
163
+ console.log('Server running at http://localhost:5000')
164
+ );