2026-01-20 09:02:26 -06:00

314 lines
10 KiB
TypeScript

// routes/landlord/onboarding/profile.tsx
import { define } from "../../../utils.ts";
import { page } from "fresh";
import type { SessionCookie } from "../../auth/callback.ts";
import { getUserById, updateUser } from "../../../services/userService.ts";
import {
getOnboardingStatus,
setOnboardingStep,
} from "../../../services/onboardingService.ts";
import { User } from "../../../db/interfaces.ts";
import { createPublicId } from "../../../helpers.ts";
export const handler = define.handlers({
async GET(ctx) {
const stateData = ctx.state.data as SessionCookie;
const { user_id } = stateData;
const publicId = stateData.user_id
? await createPublicId(stateData.user_id)
: "";
if (!user_id) {
return new Response("", {
status: 302,
headers: { Location: "/auth/login" },
});
}
const status = await getOnboardingStatus(user_id);
const onboardingStep = status?.onboarding_step ?? 0;
if (status?.onboarded_landlord && onboardingStep === 0) {
return new Response("", {
status: 303,
headers: { Location: "/landlord/properties/add" },
});
}
const user = await getUserById(user_id);
return page({ stateData, user: user ? { ...user, id: publicId } : null });
},
async POST(ctx) {
const stateData = ctx.state.data as SessionCookie;
const { user_id } = stateData;
const publicId = stateData.user_id
? await createPublicId(stateData.user_id)
: "";
if (!user_id) {
return new Response("", {
status: 302,
headers: { Location: "/auth/login" },
});
}
const status = await getOnboardingStatus(user_id);
const onboardingStep = status?.onboarding_step ?? 0;
if (status?.onboarded_landlord && onboardingStep === 0) {
return new Response("", {
status: 303,
headers: { Location: "/landlord/properties/add" },
});
}
const formData = await ctx.req.formData();
const phone = (formData.get("phone") || "").toString().trim();
const address_1 = (formData.get("address_1") || "").toString().trim();
const city = (formData.get("city") || "").toString().trim();
const state = (formData.get("state") || "").toString().trim();
const zip = (formData.get("zip") || "").toString().trim();
const country = (formData.get("country") || "").toString().trim();
if (!phone || !address_1 || !city || !state || !zip || !country) {
const user = await getUserById(user_id);
return page({
stateData,
user: user ? { ...user, id: publicId } : null,
error: "Phone and full address are required.",
});
}
await updateUser(user_id, {
name: (formData.get("name") || "").toString(),
email: (formData.get("email") || "").toString(),
phone: phone || "",
address_1: address_1 || "",
address_2: (formData.get("address_2") || "").toString(),
city: city || "",
state: state || "",
zip: zip || "",
country: country || "",
user_type: "landlord",
onboarded_landlord: true,
} as Partial<User>);
await setOnboardingStep(user_id, 2);
return new Response("", {
status: 302,
headers: { Location: "/landlord/onboarding/property-setup" },
});
},
});
export default define.page(function LandlordProfileSetup({ data }) {
const { stateData, user, error } = data as {
stateData: SessionCookie;
user: any;
error?: string;
};
return (
<div class="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
Complete Your Landlord Profile
</h2>
<p class="mt-2 text-center text-sm text-gray-600">
Tell us about yourself to get started.
</p>
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-2xl">
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
{error && (
<div class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<form method="POST" class="space-y-6">
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label
for="name"
class="block text-sm font-medium text-gray-700"
>
Full Name *
</label>
<input
id="name"
name="name"
type="text"
required
value={user?.name || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label
for="email"
class="block text-sm font-medium text-gray-700"
>
Email Address *
</label>
<input
id="email"
name="email"
type="email"
required
value={user?.email || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label
for="phone"
class="block text-sm font-medium text-gray-700"
>
Phone Number *
</label>
<input
id="phone"
name="phone"
type="tel"
required
value={user?.phone || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div class="sm:col-span-2">
<label
for="address_1"
class="block text-sm font-medium text-gray-700"
>
Street Address *
</label>
<input
id="address_1"
name="address_1"
type="text"
required
value={user?.address_1 || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div class="sm:col-span-2">
<label
for="address_2"
class="block text-sm font-medium text-gray-700"
>
Apartment, suite, etc. (optional)
</label>
<input
id="address_2"
name="address_2"
type="text"
value={user?.address_2 || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label
for="city"
class="block text-sm font-medium text-gray-700"
>
City *
</label>
<input
id="city"
name="city"
type="text"
required
value={user?.city || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label
for="state"
class="block text-sm font-medium text-gray-700"
>
State/Province *
</label>
<input
id="state"
name="state"
type="text"
required
value={user?.state || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label
for="zip"
class="block text-sm font-medium text-gray-700"
>
ZIP/Postal Code *
</label>
<input
id="zip"
name="zip"
type="text"
required
value={user?.zip || ""}
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label
for="country"
class="block text-sm font-medium text-gray-700"
>
Country *
</label>
<select
id="country"
name="country"
required
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<option value="US" selected={user?.country === "US"}>
United States
</option>
<option value="CA" selected={user?.country === "CA"}>
Canada
</option>
<option value="UK" selected={user?.country === "UK"}>
United Kingdom
</option>
<option value="AU" selected={user?.country === "AU"}>
Australia
</option>
</select>
</div>
</div>
<div class="flex justify-end pt-6">
<button
type="submit"
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Continue to Properties
</button>
</div>
</form>
</div>
</div>
</div>
);
});