Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
042e9a8
Finished Landlord Messaging Feature
parsa-tehranipoor May 27, 2025
2e9113b
Merged pulled code with current code
parsa-tehranipoor May 27, 2025
6c878ad
Fixed minor error-detection bug
parsa-tehranipoor May 27, 2025
175c655
Added data tab with apartment data in admin page
CasperL1218 Sep 22, 2025
fca6e1c
doc for data tab
CasperL1218 Sep 22, 2025
684e0e9
Fixed UX Bugs, display components, and added documentation
parsa-tehranipoor Sep 22, 2025
f91602b
Merge branch 'main' into bugs-in-reviews
parsa-tehranipoor Sep 22, 2025
7497b57
add dependencies
CasperL1218 Sep 24, 2025
1f98ab2
add export apartment name script
CasperL1218 Sep 24, 2025
a85704d
Finishing touches: fixed calendar pop up bug
parsa-tehranipoor Oct 1, 2025
59c7231
Final touches
parsa-tehranipoor Oct 1, 2025
013a576
Final Touches(last one I swear)
parsa-tehranipoor Oct 1, 2025
590a533
Implemented Admin Page Apartment Editing UI\
CasperL1218 Oct 6, 2025
9799fa1
Implement folder feature endpoints
Oct 22, 2025
dc20dc9
Implement frontend for the folder system
Oct 27, 2025
6f508e3
Added more routes plus documentation
parsa-tehranipoor Oct 27, 2025
95c56c7
Update FolderDetailPage to display apartment cards
Oct 27, 2025
23f0656
Add documentation for folder api routes
Oct 29, 2025
03b8939
Implement folder editing
Nov 2, 2025
8b53567
Update apartment removal
Nov 2, 2025
c5e6237
Implement clicking into map pins
Nov 6, 2025
f7ab07c
Users must submit a review when submitting a new apartment
Nov 6, 2025
47945c9
Prompt to add apartment in search results
Nov 11, 2025
3fea632
Finished testing, migrated data in firebase, and tweaked routes accor…
parsa-tehranipoor Nov 17, 2025
85b0d8b
Add documentation for Folder components
Nov 19, 2025
f5f8c65
Refactor apartment schema to support multiple room types
CasperL1218 Nov 21, 2025
f7a2dfb
Implement Room Types Admin UI and Display Utilities
CasperL1218 Nov 21, 2025
887d5ca
Implement apartment card display and search filtering for room types
CasperL1218 Nov 21, 2025
3831489
Enhance Admin Apartment Data tab with search, filters, and improved l…
CasperL1218 Nov 21, 2025
f26cff8
Add Create New Apartment functionality to Admin Data tab
CasperL1218 Nov 21, 2025
e2839fe
Fix search UX and improve results page layout
CasperL1218 Nov 22, 2025
c7b1f20
Put folders in Bookmarks page
Nov 22, 2025
51c4576
Fix fetching apartments
Nov 22, 2025
6b373e2
fix routing to saved apartments
Nov 25, 2025
70e837c
1. package.json (root):
CasperL1218 Dec 3, 2025
afc771f
Files changed:
CasperL1218 Dec 3, 2025
fea1cee
format changes
CasperL1218 Dec 3, 2025
64c775d
fix lint error
CasperL1218 Dec 3, 2025
96f400a
Fixed dependency issues, edited AdminPage to include blog post creati…
parsa-tehranipoor Dec 3, 2025
4a74273
Implement add to folder popover
Dec 5, 2025
4be7be0
Implement folder design
Dec 5, 2025
c738fe2
Update popover styling
Dec 5, 2025
b83326d
Fix repeated re-rendering
Dec 8, 2025
5fa50b1
Finalize FolderSection frontend
Dec 8, 2025
0ff6c83
Add new developer Parsa Tehranipoor to Contributors list on README file
parsa-tehranipoor Dec 8, 2025
7bcd238
Fix Firebase User import and add Apartment component documentation
CasperL1218 Dec 8, 2025
b7c923c
Continued to work on the admin side of the blog post feature via UI f…
parsa-tehranipoor Dec 8, 2025
3d8b6b0
Fix margin for create folder button
Dec 8, 2025
1bc0287
Flag deprecated endpoints for saving apartments and landlords
Dec 8, 2025
3f221ed
Added documentation to BlogPostPage and BlogPostDetailPage files
parsa-tehranipoor Dec 8, 2025
9e18231
Completed BlogPost Admin page for adding new blogposts
parsa-tehranipoor Jan 26, 2026
456bb39
Update frontend
laurenp-2 Feb 20, 2026
c4de3c9
Bulk update of apartment data
laurenp-2 Feb 27, 2026
6005238
Update documentation
laurenp-2 Mar 2, 2026
e362fb5
Implement unit tests for helper functions
laurenp-2 Mar 2, 2026
5a081e7
Fix pre-commit hook: upgrade frontend TS to
CasperL1218 Mar 22, 2026
5e37da6
Fix MapInfo JSDoc to follow
CasperL1218 Mar 23, 2026
70b5f11
Merge branch 'apartment-component-documentation'
CasperL1218 Mar 23, 2026
7dca674
Revert frontend save UI changes — let folder_system branch own that t…
CasperL1218 Mar 23, 2026
df939a4
Merge branch 'deprecated-endpoints'
CasperL1218 Mar 23, 2026
f211b0b
Merge remote-tracking branch 'origin/data-update'
CasperL1218 Mar 23, 2026
4375056
Fix review field helperText using wrong error state, remove unused us…
CasperL1218 Mar 23, 2026
e9c4ed7
Merge branch 'bug_fixes'
CasperL1218 Mar 23, 2026
2f47189
Fix email endpoint to use req.body, fix error toast severity, remove …
CasperL1218 Mar 23, 2026
c58edd9
Merge branch 'bugs-in-reviews'
CasperL1218 Mar 23, 2026
c7b50d0
Fix migration to preserve legacy numBeds/numBaths/price as RoomType e…
CasperL1218 Mar 23, 2026
f8c2129
Merge branch 'admin-data-edit'
CasperL1218 Mar 23, 2026
0ac6b13
Fix Folder type field name: apartmentIds → apartments
CasperL1218 Mar 23, 2026
7c8de6c
Merge branch 'folder_system' into main
CasperL1218 Mar 23, 2026
e01f91e
Merge main into blog-post-feature; resolve conflicts and prep for merge
CasperL1218 Mar 23, 2026
88d3281
Add admin whitelist management feature
CasperL1218 Mar 23, 2026
feeef9d
Fix Prettier formatting on authAdmin.ts and LandlordPage.tsx
CasperL1218 Mar 23, 2026
417a462
Fix engines node version: 16.x → >=18 to match CI
CasperL1218 Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to note this as a breaking change in the PR description (I got dependency version issues with ts-node and a few other dependencies as a result)

- name: Use Yarn Cache
uses: actions/cache@v3
with:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/firebase-hosting-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ jobs:
environment: prod
steps:
- uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 18
- run: yarn install
- run: yarn workspace frontend build
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase-hosting-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- run: yarn install
- run: yarn workspace frontend build
env:
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ yarn-error.log*
*.code-workspace
**/.idea

# Local reference files (not for repo)
TODO.md
DOCS_GUIDE.md
BLOG_TODO.md

# Misc
.DS_Store
.env
Expand Down
4 changes: 4 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"add_buildings": "ts-node scripts/add_buildings.ts",
"add_landlords": "ts-node scripts/add_landlords.ts",
"add_reviews": "ts-node scripts/add_reviews_nodups.ts",
"export_apartments": "env-cmd -f ../.env.prod ts-node scripts/export_apartments.ts",
"update_apartments": "env-cmd -f ../.env.prod ts-node scripts/update_apartments_from_csv.ts",
"export_apartment_names": "ts-node scripts/export_apartment_names.ts",
"init_blogposts": "env-cmd -f ../.env.prod ts-node scripts/init_blogposts_collection.ts",
"build": "tsc",
"tsc": "tsc",
"start": "node dist/backend/src/server.js",
Expand Down
4 changes: 1 addition & 3 deletions backend/scripts/add_buildings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,11 @@ const formatBuilding = ({
name,
address,
landlordId: landlordId.toString(),
numBaths: 0,
numBeds: 0,
roomTypes: [], // Initialize with empty room types
photos: [],
area: getAreaType(area),
latitude,
longitude,
price: 0,
distanceToCampus: 0,
});

Expand Down
89 changes: 89 additions & 0 deletions backend/scripts/export_apartment_names.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import axios from 'axios';
import * as fs from 'fs';
import * as path from 'path';

// Define the structure of the API response based on the AdminPage usage
interface ApartmentData {
buildingData: {
id: string;
name: string;
address: string;
area: string;
numBeds: number;
numBaths: number;
landlordId: string;
photos: string[];
urlName: string;
latitude: number;
longitude: number;
distanceToCampus: number;
};
numReviews: number;
company: string;
avgRating: number;
avgPrice: number;
}

interface ApiResponse {
buildingData: ApartmentData[];
isEnded: boolean;
}

/**
* Script to export apartment names to a CSV file
*
* This script fetches all apartment data from the API endpoint used in AdminPage
* and exports just the apartment names to a CSV file.
*/
const exportApartmentNames = async () => {
try {
console.log('Fetching apartment data...');

// Use the same API endpoint as AdminPage
const response = await axios.get<ApiResponse>(
'http://localhost:8080/api/page-data/home/1000/numReviews'
);

if (!response.data || !response.data.buildingData) {
throw new Error('No apartment data received from API');
}

const apartments = response.data.buildingData;
console.log(`Found ${apartments.length} apartments`);

// Extract apartment names
const apartmentNames = apartments.map((apt) => apt.buildingData.name);

// Create CSV content
const csvHeader = 'Apartment Name\n';
const csvContent = apartmentNames.map((name) => `"${name}"`).join('\n');
const fullCsvContent = csvHeader + csvContent;

// Write to CSV file
const outputPath = path.join(__dirname, 'apartment_names.csv');
fs.writeFileSync(outputPath, fullCsvContent, 'utf8');

console.log(`Successfully exported ${apartmentNames.length} apartment names to: ${outputPath}`);
console.log('First few apartment names:');
apartmentNames.slice(0, 5).forEach((name, index) => {
console.log(`${index + 1}. ${name}`);
});
} catch (error) {
console.error('Error exporting apartment names:', error);

if (axios.isAxiosError(error)) {
if (error.code === 'ECONNREFUSED') {
console.error(
'Could not connect to the server. Make sure the backend server is running on localhost:3000'
);
} else {
console.error('API request failed:', error.message);
}
}

process.exit(1);
}
};

// Run the script
exportApartmentNames();
101 changes: 101 additions & 0 deletions backend/scripts/export_apartments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as fs from 'fs';
import * as path from 'path';
import { db } from '../src/firebase-config';

/**
* export_apartments.ts
*
* Exports all apartment documents from Firestore to a CSV file.
* The business team can then edit the CSV and pass it to update_apartments_from_csv.ts.
*
* Usage:
* env-cmd -f ../.env.prod ts-node scripts/export_apartments.ts
*
* Output:
* backend/scripts/apartments_export.csv
*/

const buildingCollection = db.collection('buildings');

// TODO: might have to change header fields or order (talk with business)
const CSV_HEADERS = [
'id',
'name',
'address',
'landlordId',
'numBeds',
'numBaths',
'price',
'area',
'latitude',
'longitude',
'distanceToCampus',
];

// escape CSV field values
export const escapeCSVField = (value: unknown): string => {
const str = value === null || value === undefined ? '' : String(value);
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
return `"${str.replace(/"/g, '""')}"`;
}
return str;
};

const exportApartments = async () => {
console.log('Fetching apartments from Firestore...');

const snapshot = await buildingCollection.get();

if (snapshot.empty) {
console.log('No apartments found in the database.');
process.exit(0);
}

console.log(`Found ${snapshot.docs.length} apartments. Writing CSV...`);

const rows: string[] = [CSV_HEADERS.join(',')];

snapshot.docs.forEach((doc) => {
const data = doc.data();
const row = [
doc.id,
data.name,
data.address,
data.landlordId,
data.numBeds,
data.numBaths,
data.price,
data.area,
data.latitude,
data.longitude,
data.distanceToCampus,
]
.map(escapeCSVField)
.join(',');

rows.push(row);
});

const outputPath = path.join(__dirname, 'apartments_export.csv');
fs.writeFileSync(outputPath, rows.join('\n'), 'utf8');

console.log(`Export complete: ${outputPath}`);
console.log(` ${snapshot.docs.length} apartments exported.`);
console.log('');
console.log('Next steps:');
console.log(' 1. Open apartments_export.csv in Excel or Google Sheets');
console.log(' 2. Edit the fields you want to update (do NOT change the id column)');
console.log(' 3. Save as CSV');
console.log(' 4. Run: env-cmd -f ../.env.prod ts-node scripts/update_apartments_from_csv.ts');

process.exit(0);
};

if (require.main === module) {
exportApartments().catch((err) => {
console.error('Export failed:', err);
process.exit(1);
});
}

export default exportApartments;
61 changes: 61 additions & 0 deletions backend/scripts/init_blogposts_collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* init_blogposts_collection.ts
*
* One-time idempotent script to ensure the `blogposts` Firestore collection exists
* with the correct schema. Creates a sentinel document if the collection is empty.
*
* Safe to run multiple times — never overwrites existing documents.
*
* Usage: ts-node scripts/init_blogposts_collection.ts
*/

import { db } from '../src/firebase-config';

const blogPostCollection = db.collection('blogposts');

const initBlogPostsCollection = async (): Promise<void> => {
console.log('Checking blogposts collection...');

const snapshot = await blogPostCollection.limit(1).get();

if (!snapshot.empty) {
console.log(`Collection already has ${snapshot.size}+ document(s). No initialization needed.`);
return;
}

console.log('Collection is empty. Creating sentinel document...');

const sentinelDoc = blogPostCollection.doc('_init');
const existing = await sentinelDoc.get();

if (existing.exists) {
console.log('Sentinel document already exists. Done.');
return;
}

await sentinelDoc.set({
title: '[INIT] Collection initialized',
content: '',
blurb: '',
date: new Date(),
tags: [],
visibility: 'DELETED',
likes: 0,
saves: 0,
coverImageUrl: '',
userId: null,
});

console.log('Sentinel document created at blogposts/_init with visibility=DELETED.');
console.log('Collection is ready. This sentinel document is safe to delete.');
};

initBlogPostsCollection()
.then(() => {
console.log('Done.');
process.exit(0);
})
.catch((err) => {
console.error('Error initializing blogposts collection:', err);
process.exit(1);
});
Loading
Loading