himanshu-toolkit 1.0.1 → 1.0.2

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/App1.jsx ADDED
@@ -0,0 +1,305 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import 'bootstrap/dist/css/bootstrap.min.css';
3
+
4
+ const API = 'http://localhost:5000/api/properties'; // Keep this URL only if your backend uses /api/cars.
5
+ const emptyProperty = {
6
+ ownerName: '', propertyType: '', propertyLocation: '',
7
+ numberOfBedrooms: '', furnishingType: '', monthlyRent: '',
8
+ securityDeposit: '', availablefromDate: ''
9
+ };
10
+
11
+ // FIX: These options are used to populate the Number of Bedrooms/Property Size dropdown.
12
+ const propertyOptions = {
13
+ Flat: ['1BHK', '2BHK', '3BHK', '4BHK'],
14
+ Bunglow: ['2000sqft', '3000sqft', '5000sqft'],
15
+ Shop: ['Ground Floor', 'First Floor', 'Multidimensional'],
16
+ Office: ['100', '500', '1000']
17
+ };
18
+ const furnishingOptions = ['Furnished', 'Semi-Furnished', 'Unfurnished'];
19
+ const get = (item, camel, snake) => item[camel] ?? item[snake] ?? '';
20
+
21
+ export default function App() {
22
+ const [property, setProperty] = useState({ ...emptyProperty });
23
+ // FIX: The original declared `property` state twice and used an undefined `setCars`.
24
+ const [properties, setProperties] = useState([]);
25
+ const [editId, setEditId] = useState(null);
26
+ const [success, setSuccess] = useState('');
27
+ const [error, setError] = useState('');
28
+
29
+ const fetchProperty = async () => {
30
+ try {
31
+ const res = await fetch(API);
32
+ if (!res.ok) throw new Error('Failed to fetch property records');
33
+ setProperties(await res.json());
34
+ } catch (err) {
35
+ console.error('Fetch error:', err);
36
+ setError('Could not load properties. Check your backend server.');
37
+ }
38
+ };
39
+
40
+ useEffect(() => { fetchProperty(); }, []);
41
+
42
+ const onTextChange = ({ target: { name, value } }) => {
43
+ // FIX: Use setProperty, not the undefined setCar; reset the dependent dropdown when type changes.
44
+ setProperty(prev => ({
45
+ ...prev,
46
+ [name]: value,
47
+ ...(name === 'propertyType' ? { numberOfBedrooms: '' } : {})
48
+ }));
49
+ setSuccess('');
50
+ setError('');
51
+ };
52
+
53
+ const validateForm = () => {
54
+ if (!property.ownerName.trim() || !property.propertyType ||
55
+ !property.propertyLocation.trim() || !property.numberOfBedrooms ||
56
+ !property.furnishingType || property.monthlyRent === '' ||
57
+ property.securityDeposit === '' || !property.availablefromDate)
58
+ return 'Please fill in all required fields.';
59
+
60
+ if (property.ownerName.trim().length > 25)
61
+ return 'Owner name cannot exceed 25 characters.';
62
+ if (!propertyOptions[property.propertyType]?.includes(property.numberOfBedrooms))
63
+ return 'Please select a valid property size.';
64
+ if (!furnishingOptions.includes(property.furnishingType))
65
+ return 'Please select a valid furnishing type.';
66
+
67
+ const rent = Number(property.monthlyRent);
68
+ const deposit = Number(property.securityDeposit);
69
+ if (!Number.isFinite(rent) || rent <= 0)
70
+ return 'Monthly rent must be greater than zero.';
71
+ if (!Number.isFinite(deposit) || deposit < 0)
72
+ return 'Security deposit must be zero or greater.';
73
+
74
+ // FIX: A date input returns YYYY-MM-DD, not a number. Compare date strings in this format.
75
+ const today = new Date().toISOString().slice(0, 10);
76
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(property.availablefromDate) ||
77
+ property.availablefromDate < today)
78
+ return 'Available-from date must be today or in the future.';
79
+
80
+ return '';
81
+ };
82
+
83
+ const resetForm = () => {
84
+ setProperty({ ...emptyProperty });
85
+ setEditId(null);
86
+ };
87
+
88
+ const handleSaveRecord = async e => {
89
+ e.preventDefault();
90
+ setSuccess('');
91
+ setError('');
92
+
93
+ const validationError = validateForm();
94
+ if (validationError) return setError(validationError);
95
+
96
+ const payload = {
97
+ ...property,
98
+ ownerName: property.ownerName.trim(),
99
+ propertyLocation: property.propertyLocation.trim(),
100
+ monthlyRent: Number(property.monthlyRent),
101
+ securityDeposit: Number(property.securityDeposit)
102
+ // FIX: Keep the date as YYYY-MM-DD; do not convert it to Number().
103
+ };
104
+
105
+ try {
106
+ const res = await fetch(editId !== null ? `${API}/${editId}` : API, {
107
+ method: editId !== null ? 'PUT' : 'POST',
108
+ headers: { 'Content-Type': 'application/json' },
109
+ body: JSON.stringify(payload)
110
+ });
111
+ const result = await res.json();
112
+ if (!res.ok) throw new Error(result.error || 'Failed to save property details.');
113
+
114
+ setSuccess(editId !== null
115
+ ? 'Property details updated successfully!'
116
+ : 'Property listed successfully!');
117
+ resetForm();
118
+ await fetchProperty();
119
+ } catch (err) {
120
+ console.error('Save error:', err);
121
+ setError(err.message);
122
+ }
123
+ };
124
+
125
+ const handleEdit = item => {
126
+ // FIX: The original used setCar and omitted the date; load every property field into setProperty.
127
+ setProperty({
128
+ ownerName: get(item, 'ownerName', 'owner_name'),
129
+ propertyType: get(item, 'propertyType', 'property_type'),
130
+ propertyLocation: get(item, 'propertyLocation', 'property_location'),
131
+ numberOfBedrooms: get(item, 'numberOfBedrooms', 'number_of_bedrooms'),
132
+ furnishingType: get(item, 'furnishingType', 'furnishing_type'),
133
+ monthlyRent: String(get(item, 'monthlyRent', 'monthly_rent')),
134
+ securityDeposit: String(get(item, 'securityDeposit', 'security_deposit')),
135
+ availablefromDate: String(get(item, 'availablefromDate', 'available_from_date')).slice(0, 10)
136
+ });
137
+ setEditId(item.id ?? item.ID);
138
+ setSuccess('');
139
+ setError('');
140
+ window.scrollTo({ top: 0, behavior: 'smooth' });
141
+ };
142
+
143
+ const handleDelete = async id => {
144
+ if (!window.confirm('Are you sure you want to delete this property listing?')) return;
145
+ setSuccess('');
146
+ setError('');
147
+
148
+ try {
149
+ const res = await fetch(`${API}/${id}`, { method: 'DELETE' });
150
+ const result = await res.json();
151
+ if (!res.ok) throw new Error(result.error || 'Failed to delete property listing.');
152
+ if (String(editId) === String(id)) resetForm();
153
+ setSuccess('Property listing deleted successfully!');
154
+ await fetchProperty();
155
+ } catch (err) {
156
+ console.error('Delete error:', err);
157
+ setError(err.message);
158
+ }
159
+ };
160
+
161
+ const handleCancel = () => {
162
+ resetForm();
163
+ setSuccess('');
164
+ setError('');
165
+ };
166
+
167
+ return (
168
+ <div className="container mt-5 mb-5">
169
+ <h2 className="text-center mb-2">Property Rental Management</h2>
170
+ <p className="text-center text-muted mb-4">List your property for rent</p>
171
+
172
+ {success && <div className="alert alert-success" role="status">{success}</div>}
173
+ {error && <div className="alert alert-danger" role="alert">{error}</div>}
174
+
175
+ <div className="card shadow p-4 mb-5">
176
+ <h4 className="mb-3">{editId !== null ? 'Edit Property Listing' : 'List Your Property'}</h4>
177
+ <form onSubmit={handleSaveRecord}>
178
+ <div className="row">
179
+ <div className="col-md-6 mb-3">
180
+ <label className="form-label">Owner Name</label>
181
+ <input type="text" name="ownerName" className="form-control"
182
+ value={property.ownerName} onChange={onTextChange} maxLength={25}
183
+ placeholder="Enter owner name" required />
184
+ <small className="text-muted">Maximum 25 characters</small>
185
+ </div>
186
+
187
+ <div className="col-md-6 mb-3">
188
+ <label className="form-label">Property Type</label>
189
+ <select name="propertyType" className="form-select"
190
+ value={property.propertyType} onChange={onTextChange} required>
191
+ <option value="">Select property type</option>
192
+ {Object.keys(propertyOptions).map(type =>
193
+ <option key={type} value={type}>{type}</option>
194
+ )}
195
+ </select>
196
+ </div>
197
+
198
+ <div className="col-md-6 mb-3">
199
+ <label className="form-label">Property Size / Bedrooms</label>
200
+ {/* FIX: Use the correct field name and property state, not numberOfBedroomss/car.carModel. */}
201
+ <select name="numberOfBedrooms" className="form-select"
202
+ value={property.numberOfBedrooms} onChange={onTextChange}
203
+ disabled={!property.propertyType} required>
204
+ <option value="">Select property size</option>
205
+ {(propertyOptions[property.propertyType] || []).map(option =>
206
+ <option key={option} value={option}>{option}</option>
207
+ )}
208
+ </select>
209
+ </div>
210
+
211
+ <div className="col-md-6 mb-3">
212
+ <label className="form-label">Property Location</label>
213
+ <input type="text" name="propertyLocation" className="form-control"
214
+ value={property.propertyLocation} onChange={onTextChange}
215
+ placeholder="Enter property location" required />
216
+ </div>
217
+
218
+ <div className="col-md-6 mb-3">
219
+ <label className="form-label">Furnishing Type</label>
220
+ {/* FIX: Use furnishingType as the field name and furnishingOptions as the options array. */}
221
+ <select name="furnishingType" className="form-select"
222
+ value={property.furnishingType} onChange={onTextChange} required>
223
+ <option value="">Select furnishing type</option>
224
+ {furnishingOptions.map(option =>
225
+ <option key={option} value={option}>{option}</option>
226
+ )}
227
+ </select>
228
+ </div>
229
+
230
+ <div className="col-md-6 mb-3">
231
+ <label className="form-label">Monthly Rent (₹)</label>
232
+ <input type="number" name="monthlyRent" className="form-control"
233
+ value={property.monthlyRent} onChange={onTextChange}
234
+ min="0.01" step="0.01" placeholder="Enter monthly rent" required />
235
+ </div>
236
+
237
+ <div className="col-md-6 mb-3">
238
+ <label className="form-label">Security Deposit (₹)</label>
239
+ <input type="number" name="securityDeposit" className="form-control"
240
+ value={property.securityDeposit} onChange={onTextChange}
241
+ min="0" step="0.01" placeholder="Enter security deposit" required />
242
+ </div>
243
+
244
+ <div className="col-md-6 mb-3">
245
+ <label className="form-label">Available From</label>
246
+ <input type="date" name="availablefromDate" className="form-control"
247
+ value={property.availablefromDate} onChange={onTextChange}
248
+ min={new Date().toISOString().slice(0, 10)} required />
249
+ </div>
250
+ </div>
251
+
252
+ <button type="submit" className="btn btn-primary">
253
+ {editId !== null ? 'Update Property' : 'List Property'}
254
+ </button>
255
+ {editId !== null &&
256
+ <button type="button" className="btn btn-secondary ms-2" onClick={handleCancel}>
257
+ Cancel
258
+ </button>
259
+ }
260
+ </form>
261
+ </div>
262
+
263
+ <h4 className="mb-3">Property Listings</h4>
264
+ <div className="table-responsive">
265
+ {/* FIX: The original table displayed car fields. These columns now match the property form. */}
266
+ <table className="table table-bordered table-striped table-hover align-middle">
267
+ <thead className="table-dark">
268
+ <tr>
269
+ <th>ID</th><th>Owner</th><th>Type</th><th>Location</th>
270
+ <th>Bedrooms / Size</th><th>Furnishing</th><th>Monthly Rent</th>
271
+ <th>Deposit</th><th>Available From</th><th>Actions</th>
272
+ </tr>
273
+ </thead>
274
+ <tbody>
275
+ {!properties.length ? (
276
+ <tr><td colSpan="10" className="text-center">No property listings found</td></tr>
277
+ ) : properties.map(item => {
278
+ const id = item.id ?? item.ID;
279
+ return (
280
+ <tr key={id}>
281
+ <td>{id}</td>
282
+ <td>{get(item, 'ownerName', 'owner_name')}</td>
283
+ <td>{get(item, 'propertyType', 'property_type')}</td>
284
+ <td>{get(item, 'propertyLocation', 'property_location')}</td>
285
+ <td>{get(item, 'numberOfBedrooms', 'number_of_bedrooms')}</td>
286
+ <td>{get(item, 'furnishingType', 'furnishing_type')}</td>
287
+ <td>₹{Number(get(item, 'monthlyRent', 'monthly_rent')).toLocaleString('en-IN')}</td>
288
+ <td>₹{Number(get(item, 'securityDeposit', 'security_deposit')).toLocaleString('en-IN')}</td>
289
+ <td>{String(get(item, 'availablefromDate', 'available_from_date')).slice(0, 10)}</td>
290
+ <td>
291
+ <button type="button" className="btn btn-warning btn-sm me-2 mb-1"
292
+ onClick={() => handleEdit(item)}>Edit</button>
293
+ <button type="button" className="btn btn-danger btn-sm mb-1"
294
+ onClick={() => handleDelete(id)}>Delete</button>
295
+ </td>
296
+ </tr>
297
+ );
298
+ })}
299
+ </tbody>
300
+ </table>
301
+ </div>
302
+ </div>
303
+ );
304
+ }
305
+
package/database1.sql ADDED
@@ -0,0 +1,17 @@
1
+ CREATE DATABASE IF NOT EXISTS property_rental_db;
2
+ USE property_rental_db;
3
+
4
+ CREATE TABLE IF NOT EXISTS properties (
5
+ id INT AUTO_INCREMENT PRIMARY KEY,
6
+ owner_name VARCHAR(25) NOT NULL,
7
+ property_type ENUM('Flat', 'Bunglow', 'Shop', 'Office') NOT NULL,
8
+ property_location VARCHAR(255) NOT NULL,
9
+ number_of_bedrooms VARCHAR(50) NOT NULL,
10
+ furnishing_type ENUM('Furnished', 'Semi-Furnished', 'Unfurnished') NOT NULL,
11
+ monthly_rent DECIMAL(12,2) NOT NULL,
12
+ security_deposit DECIMAL(12,2) NOT NULL DEFAULT 0,
13
+ available_from_date DATE NOT NULL,
14
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
15
+ );
16
+
17
+ select *from properties;
package/package.json CHANGED
@@ -1,18 +1,22 @@
1
1
  {
2
2
  "name": "himanshu-toolkit",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "My reusable full-stack application toolkit",
5
5
  "main": "server.js",
6
6
  "files": [
7
7
  "App.jsx",
8
+ "App1.jsx",
9
+ "database.sql",
10
+ "database1.sql",
8
11
  "server.js",
9
- "database.sql"
12
+ "server1.js"
10
13
  ],
11
14
  "keywords": [
12
15
  "react",
13
16
  "nodejs",
14
17
  "mysql",
15
- "fullstack"
18
+ "fullstack",
19
+ "utilities"
16
20
  ],
17
21
  "author": "Himanshu",
18
22
  "license": "MIT",
package/server1.js ADDED
@@ -0,0 +1,182 @@
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: 'property_rental_db'
14
+ });
15
+
16
+ db.connect(err => {
17
+ if (err) return console.error('MySQL connection error:', err.message);
18
+ console.log('Connected to Property Rental Database!');
19
+ });
20
+
21
+ const propertySizes = {
22
+ Flat: ['1BHK', '2BHK', '3BHK', '4BHK'],
23
+ Bunglow: ['2000sqft', '3000sqft', '5000sqft'],
24
+ Shop: ['Ground Floor', 'First Floor', 'Multidimensional'],
25
+ Office: ['100', '500', '1000']
26
+ };
27
+
28
+ const furnishingTypes = ['Furnished', 'Semi-Furnished', 'Unfurnished'];
29
+
30
+ const columns = [
31
+ 'ownerName', 'propertyType', 'propertyLocation',
32
+ 'numberOfBedrooms', 'furnishingType', 'monthlyRent',
33
+ 'securityDeposit', 'availablefromDate'
34
+ ];
35
+
36
+ const validateProperty = p => {
37
+ if (typeof p.ownerName !== 'string' ||
38
+ !p.ownerName.trim() || p.ownerName.trim().length > 25)
39
+ return 'Owner name is required and cannot exceed 25 characters.';
40
+
41
+ if (!Object.hasOwn(propertySizes, p.propertyType))
42
+ return 'Please select a valid property type.';
43
+
44
+ if (typeof p.propertyLocation !== 'string' || !p.propertyLocation.trim())
45
+ return 'Property location is required.';
46
+
47
+ if (!propertySizes[p.propertyType].includes(p.numberOfBedrooms))
48
+ return 'Please select a valid size for the property type.';
49
+
50
+ if (!furnishingTypes.includes(p.furnishingType))
51
+ return 'Please select a valid furnishing type.';
52
+
53
+ for (const field of ['monthlyRent', 'securityDeposit']) {
54
+ const value = Number(p[field]);
55
+ if (p[field] === '' || p[field] == null ||
56
+ !Number.isFinite(value) || value < 0 ||
57
+ (field === 'monthlyRent' && value === 0))
58
+ return `${field} must be a valid ${field === 'monthlyRent' ? 'positive' : 'non-negative'} amount.`;
59
+ }
60
+
61
+ if (typeof p.availablefromDate !== 'string' ||
62
+ !/^\d{4}-\d{2}-\d{2}$/.test(p.availablefromDate) ||
63
+ !Number.isFinite(Date.parse(p.availablefromDate)) ||
64
+ new Date(p.availablefromDate).toISOString().slice(0, 10) !== p.availablefromDate)
65
+ return 'Please enter a valid availability date (YYYY-MM-DD).';
66
+
67
+ return null;
68
+ };
69
+
70
+ const getValues = p => [
71
+ p.ownerName.trim(),
72
+ p.propertyType,
73
+ p.propertyLocation.trim(),
74
+ p.numberOfBedrooms,
75
+ p.furnishingType,
76
+ Number(p.monthlyRent),
77
+ Number(p.securityDeposit),
78
+ p.availablefromDate
79
+ ];
80
+
81
+ const fail = (res, status, error) => res.status(status).json({ error });
82
+
83
+ app.get('/api/properties', (req, res) => {
84
+ db.query(
85
+ `SELECT id, owner_name AS ownerName,
86
+ property_type AS propertyType,
87
+ property_location AS propertyLocation,
88
+ number_of_bedrooms AS numberOfBedrooms,
89
+ furnishing_type AS furnishingType,
90
+ monthly_rent AS monthlyRent,
91
+ security_deposit AS securityDeposit,
92
+ available_from_date AS availablefromDate
93
+ FROM properties ORDER BY id DESC`,
94
+ (err, rows) => {
95
+ if (err) {
96
+ console.error('Fetch error:', err.message);
97
+ return fail(res, 500, 'Failed to fetch properties.');
98
+ }
99
+ res.json(rows);
100
+ }
101
+ );
102
+ });
103
+
104
+ app.post('/api/properties', (req, res) => {
105
+ const error = validateProperty(req.body);
106
+ if (error) return fail(res, 400, error);
107
+
108
+ const sql = `INSERT INTO properties
109
+ (owner_name, property_type, property_location,
110
+ number_of_bedrooms, furnishing_type, monthly_rent,
111
+ security_deposit, available_from_date)
112
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
113
+
114
+ db.query(sql, getValues(req.body), (err, result) => {
115
+ if (err) {
116
+ console.error('Insert error:', err.message);
117
+ return fail(res, 500, 'Failed to add property.');
118
+ }
119
+ res.status(201).json({
120
+ message: 'Property added successfully!',
121
+ id: result.insertId
122
+ });
123
+ });
124
+ });
125
+
126
+ app.put('/api/properties/:id', (req, res) => {
127
+ const id = Number(req.params.id);
128
+ if (!Number.isSafeInteger(id) || id <= 0)
129
+ return fail(res, 400, 'Invalid property ID.');
130
+
131
+ const error = validateProperty(req.body);
132
+ if (error) return fail(res, 400, error);
133
+
134
+ const sql = `UPDATE properties SET
135
+ owner_name = ?, property_type = ?, property_location = ?,
136
+ number_of_bedrooms = ?, furnishing_type = ?, monthly_rent = ?,
137
+ security_deposit = ?, available_from_date = ?
138
+ WHERE id = ?`;
139
+
140
+ db.query(sql, [...getValues(req.body), id], (err, result) => {
141
+ if (err) {
142
+ console.error('Update error:', err.message);
143
+ return fail(res, 500, 'Failed to update property.');
144
+ }
145
+ if (result.affectedRows)
146
+ return res.json({ message: 'Property updated successfully!' });
147
+
148
+ db.query('SELECT id FROM properties WHERE id = ?', [id], (err, rows) => {
149
+ if (err) return fail(res, 500, 'Failed to verify property.');
150
+ if (!rows.length) return fail(res, 404, 'Property not found.');
151
+ res.json({ message: 'Property details are already up to date.' });
152
+ });
153
+ });
154
+ });
155
+
156
+ app.delete('/api/properties/:id', (req, res) => {
157
+ const id = Number(req.params.id);
158
+ if (!Number.isSafeInteger(id) || id <= 0)
159
+ return fail(res, 400, 'Invalid property ID.');
160
+
161
+ db.query('DELETE FROM properties WHERE id = ?', [id], (err, result) => {
162
+ if (err) {
163
+ console.error('Delete error:', err.message);
164
+ return fail(res, 500, 'Failed to delete property.');
165
+ }
166
+ if (!result.affectedRows)
167
+ return fail(res, 404, 'Property not found.');
168
+
169
+ res.json({ message: 'Property deleted successfully!' });
170
+ });
171
+ });
172
+
173
+ app.use((err, req, res, next) => {
174
+ if (err instanceof SyntaxError && err.status === 400)
175
+ return fail(res, 400, 'Invalid JSON request body.');
176
+ console.error(err);
177
+ res.status(500).json({ error: 'Internal server error.' });
178
+ });
179
+
180
+ app.listen(5000, () =>
181
+ console.log('Server running at http://localhost:5000')
182
+ );