From c48fc96b33dbd4e69fc743b2c4370170521b8f8d Mon Sep 17 00:00:00 2001 From: Ryan Westfall Date: Thu, 11 Dec 2025 13:16:54 -0600 Subject: [PATCH] closes #8 --- ditch-the-agent/src/axiosApi.js | 3 +- .../Home/Profile/AddPropertyDialog.tsx | 3 +- .../Home/Profile/AttorneyProfileCard.tsx | 3 +- ditch-the-agent/src/config/features.ts | 57 +++++++++++++++ .../src/contexts/WebSocketContext.tsx | 9 +-- ditch-the-agent/src/pages/Vendors/Vendors.tsx | 3 +- .../src/pages/authentication/SignUp.tsx | 71 ++++++++++--------- 7 files changed, 107 insertions(+), 42 deletions(-) create mode 100644 ditch-the-agent/src/config/features.ts diff --git a/ditch-the-agent/src/axiosApi.js b/ditch-the-agent/src/axiosApi.js index 6b3d784..3b3f92a 100644 --- a/ditch-the-agent/src/axiosApi.js +++ b/ditch-the-agent/src/axiosApi.js @@ -1,7 +1,8 @@ import axios from 'axios'; import Cookies from 'js-cookie'; +import { features } from './config/features'; -const baseURL = import.meta.env.VITE_API_URL; +const baseURL = features.apiUrl; console.log(baseURL); export const axiosRealEstateApi = axios.create({ diff --git a/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AddPropertyDialog.tsx b/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AddPropertyDialog.tsx index ab2ca7e..d6543af 100644 --- a/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AddPropertyDialog.tsx +++ b/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AddPropertyDialog.tsx @@ -27,6 +27,7 @@ import { import { test_property_search } from 'data/mock_property_search'; import { extractLatLon } from 'utils'; import { test_autocomplete } from 'data/mock_autocomplete_results'; +import { features } from '../../../../../config/features'; interface AddPropertyDialogProps { open: boolean; @@ -204,7 +205,7 @@ const AddPropertyDialog: React.FC = ({ open, onClose, on console.log('here we go', value); if (value) { console.log('find the test data'); - const test: boolean = import.meta.env.USE_LIVE_DATA; + const test: boolean = features.useLiveData; if (test) { const parts: string[] = test_property_search.data.currentMortgages[0].recordingDate.split('T'); diff --git a/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AttorneyProfileCard.tsx b/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AttorneyProfileCard.tsx index b173ca7..672c6b1 100644 --- a/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AttorneyProfileCard.tsx +++ b/ditch-the-agent/src/components/sections/dashboard/Home/Profile/AttorneyProfileCard.tsx @@ -25,6 +25,7 @@ import { PlacePrediction } from './AddPropertyDialog'; import { test_autocomplete } from 'data/mock_autocomplete_results'; import { axiosInstance, axiosRealEstateApi } from '../../../../../axiosApi'; import { extractLatLon } from 'utils'; +import { features } from '../../../../../config/features'; interface AttorneyProfileCardProps { attorney: AttorneyAPI; @@ -115,7 +116,7 @@ const AttorneyProfileCard: React.FC = ({ event: React.SyntheticEvent, value: string, ) => { - const test: boolean = !import.meta.env.USE_LIVE_DATA; + const test: boolean = !features.useLiveData; let data: AutocompleteDataResponseAPI[] = []; if (value.length > 2) { if (test) { diff --git a/ditch-the-agent/src/config/features.ts b/ditch-the-agent/src/config/features.ts new file mode 100644 index 0000000..b35967b --- /dev/null +++ b/ditch-the-agent/src/config/features.ts @@ -0,0 +1,57 @@ +/** + * Feature flags and environment configuration + * Controls feature availability and environment settings based on build mode (production, beta, development) + */ + +interface FeatureConfig { + // Registration features + enableAttorneyRegistration: boolean; + enableRealEstateAgentRegistration: boolean; + enableRegistration: boolean; + + // API Configuration + apiUrl: string; + + // Data Configuration + useLiveData: boolean; +} + +/** + * Get feature configuration based on current environment + */ +const getFeatureConfig = (): FeatureConfig => { + const mode = import.meta.env.MODE || 'development'; + + // Production configuration + if (mode === 'production') { + return { + enableAttorneyRegistration: false, + enableRealEstateAgentRegistration: false, + enableRegistration: false, + apiUrl: 'https://backend.ditchtheagent.com/api/', + useLiveData: true, + }; + } + + // Beta configuration + if (mode === 'beta') { + return { + enableAttorneyRegistration: true, + enableRealEstateAgentRegistration: true, + enableRegistration: true, + apiUrl: 'https://beta.backend.ditchtheagent.com/api/', + useLiveData: true, + }; + } + + // Development configuration (default) + return { + enableAttorneyRegistration: true, + enableRealEstateAgentRegistration: true, + enableRegistration: true, + apiUrl: 'http://127.0.0.1:8010/api/', + useLiveData: false, + }; +}; + +export const features = getFeatureConfig(); diff --git a/ditch-the-agent/src/contexts/WebSocketContext.tsx b/ditch-the-agent/src/contexts/WebSocketContext.tsx index d25e8bf..b56e6cc 100644 --- a/ditch-the-agent/src/contexts/WebSocketContext.tsx +++ b/ditch-the-agent/src/contexts/WebSocketContext.tsx @@ -1,6 +1,7 @@ import React, { useEffect, createContext, useRef, useState, useContext, ReactNode } from 'react'; import { AccountContext } from './AccountContext'; import { AuthContext } from './AuthContext'; +import { features } from '../config/features'; // --- // Define Types and Interfaces @@ -65,10 +66,10 @@ interface WebSocketProviderProps { // Provide a default value that matches the IWebSocketContext interface. // This is used when a component tries to consume the context without a provider. const WebSocketContext = createContext({ - subscribe: () => {}, - unsubscribe: () => {}, + subscribe: () => { }, + unsubscribe: () => { }, socket: null, - sendMessages: () => {}, + sendMessages: () => { }, }); // --- @@ -141,7 +142,7 @@ function WebSocketProvider({ children }: WebSocketProviderProps) { ws.current.close(); } - const wsUrl = new URL(import.meta.env.VITE_API_URL || 'ws://127.0.0.1:8010/ws/'); + const wsUrl = new URL(features.apiUrl || 'ws://127.0.0.1:8010/ws/'); wsUrl.protocol = wsUrl.protocol.replace('http', 'ws'); ws.current = new WebSocket( diff --git a/ditch-the-agent/src/pages/Vendors/Vendors.tsx b/ditch-the-agent/src/pages/Vendors/Vendors.tsx index 51a5be6..9d3f1cd 100644 --- a/ditch-the-agent/src/pages/Vendors/Vendors.tsx +++ b/ditch-the-agent/src/pages/Vendors/Vendors.tsx @@ -20,8 +20,9 @@ import { Switch, Typography, } from '@mui/material'; +import { features } from '../../config/features'; -const base_url: string = `${import.meta.env.VITE_API_URL?.replace('/api/', '')}/media/vendor_pictures/`; +const base_url: string = `${features.apiUrl?.replace('/api/', '')}/media/vendor_pictures/`; // Define the array of corrected and alphabetized categories with 'as const' export const CATEGORY_NAMES = [ diff --git a/ditch-the-agent/src/pages/authentication/SignUp.tsx b/ditch-the-agent/src/pages/authentication/SignUp.tsx index 1ff4826..bb4d7da 100644 --- a/ditch-the-agent/src/pages/authentication/SignUp.tsx +++ b/ditch-the-agent/src/pages/authentication/SignUp.tsx @@ -24,6 +24,7 @@ import Image from 'components/base/Image'; import { axiosInstance } from '../../axiosApi.js'; import PasswordStrengthChecker from '../../components/PasswordStrengthChecker'; import { useNavigate } from 'react-router-dom'; +import { features } from '../../config/features'; type SignUpValues = { email: string; @@ -191,41 +192,43 @@ const SignUp = (): ReactElement => { {[ - { value: 'property_owner', label: 'Home Buyer/Seller', icon: 'mdi:home-account' }, - { value: 'attorney', label: 'Attorney', icon: 'mdi:gavel' }, - { value: 'vendor', label: 'Vendor', icon: 'mdi:briefcase' }, - ].map((type) => ( - setFieldValue('ownerType', type.value)} - sx={{ - p: 2, - flex: 1, - cursor: 'pointer', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - gap: 1, - borderColor: values.ownerType === type.value ? 'primary.main' : 'divider', - bgcolor: values.ownerType === type.value ? 'action.selected' : 'background.paper', - transition: 'all 0.2s', - '&:hover': { - borderColor: 'primary.main', - bgcolor: 'action.hover', - }, - }} - > - - type.enabled) + .map((type) => ( + setFieldValue('ownerType', type.value)} + sx={{ + p: 2, + flex: 1, + cursor: 'pointer', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: 1, + borderColor: values.ownerType === type.value ? 'primary.main' : 'divider', + bgcolor: values.ownerType === type.value ? 'action.selected' : 'background.paper', + transition: 'all 0.2s', + '&:hover': { + borderColor: 'primary.main', + bgcolor: 'action.hover', + }, + }} > - {type.label} - - - ))} + + + {type.label} + + + ))}