update profile and UI tweaks

This commit is contained in:
Trey t
2025-11-30 12:37:15 -06:00
parent b0838d85df
commit 94781f4c48
25 changed files with 184 additions and 103 deletions

View File

@@ -100,7 +100,7 @@ class AuthApi(private val client: HttpClient = ApiClient.httpClient) {
suspend fun updateProfile(token: String, request: UpdateProfileRequest): ApiResult<User> {
return try {
val response = client.put("$baseUrl/auth/update-profile/") {
val response = client.put("$baseUrl/auth/profile/") {
header("Authorization", "Token $token")
contentType(ContentType.Application.Json)
setBody(request)

View File

@@ -162,19 +162,6 @@ struct SmallWidgetView: View {
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Header
HStack(spacing: 6) {
Image(systemName: "house.fill")
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(.blue)
Text("Casera")
.font(.system(size: 14, weight: .bold))
.foregroundStyle(.primary)
Spacer()
}
// Task Count
VStack(alignment: .leading, spacing: 4) {
Text("\(entry.taskCount)")
@@ -410,16 +397,8 @@ struct LargeWidgetView: View {
VStack(alignment: .leading, spacing: 6) {
// Header
HStack(spacing: 6) {
Image(systemName: "house.fill")
.font(.system(size: 16, weight: .semibold))
.foregroundStyle(.blue)
Text("Casera")
.font(.system(size: 16, weight: .bold))
.foregroundStyle(.primary)
Spacer()
Text("\(entry.taskCount)")
.font(.system(size: 22, weight: .bold))
.foregroundStyle(.blue)
@@ -483,7 +462,9 @@ struct LargeTaskRowView: View {
HStack(spacing: 10) {
if let residenceName = task.residenceName {
HStack(spacing: 2) {
Image(systemName: "house.fill")
Image("icon")
.resizable()
.frame(width: 7, height: 7)
.font(.system(size: 7))
Text(residenceName)
.font(.system(size: 9))

View File

@@ -85,6 +85,13 @@
);
target = 1CBF1BEC2ECD9768001BF56C /* CaseraUITests */;
};
1C87A67A2EDCC3100081E450 /* Exceptions for "iosApp" folder in "CaseraExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Assets.xcassets,
);
target = 1C07893C2EBC218B00392B46 /* CaseraExtension */;
};
1CBF1C072ECD97AC001BF56C /* Exceptions for "CaseraTests" folder in "CaseraTests" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
@@ -135,6 +142,7 @@
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
84D9B4B86A80D013B8CBB951 /* Exceptions for "iosApp" folder in "Casera" target */,
1C87A67A2EDCC3100081E450 /* Exceptions for "iosApp" folder in "CaseraExtension" target */,
1C77EDA12ECE784100A53003 /* Exceptions for "iosApp" folder in "CaseraUITests" target */,
);
path = iosApp;

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "house_outline.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "icon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "house_outline 2.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "house_outline 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "house_outline.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -8,15 +8,15 @@ struct ContractorCard: View {
var body: some View {
HStack(spacing: AppSpacing.md) {
// Avatar
ZStack {
Circle()
.fill(Color.appPrimary.opacity(0.1))
.frame(width: 56, height: 56)
Image(systemName: "person.fill")
.font(.title2)
.foregroundColor(Color.appPrimary)
}
// ZStack {
// Circle()
// .fill(Color.appPrimary.opacity(0.1))
// .frame(width: 56, height: 56)
//
// Image(systemName: "person.fill")
// .font(.title2)
// .foregroundColor(Color.appPrimary)
// }
// Content
VStack(alignment: .leading, spacing: AppSpacing.xxs) {

View File

@@ -428,10 +428,12 @@ struct ContractorFormSheet: View {
// Set residence if contractor has one
if let residenceId = contractor.residenceId {
selectedResidenceId = residenceId.int32Value
// Try to find residence name from loaded residences
if let residences = residenceViewModel.myResidences?.residences,
let residence = residences.first(where: { $0.id == residenceId.int32Value }) {
selectedResidenceName = residence.name
if let selectedResidenceId {
ComposeApp.ResidenceViewModel().getResidence(id: selectedResidenceId, onResult: { result in
if let success = result as? ApiResultSuccess<ResidenceResponse> {
self.selectedResidenceName = success.data?.name
}
})
}
}

View File

@@ -27,14 +27,18 @@ struct DocumentCard: View {
var body: some View {
HStack(spacing: AppSpacing.md) {
// Document Icon
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(typeColor.opacity(0.1))
.frame(width: 56, height: 56)
VStack {
Image(systemName: typeIcon)
.font(.system(size: 24))
.foregroundColor(typeColor)
.background(content: {
RoundedRectangle(cornerRadius: 8)
.fill(typeColor.opacity(0.1))
.frame(width: 56, height: 56)
})
.padding(AppSpacing.md)
Spacer()
}
VStack(alignment: .leading, spacing: 4) {

View File

@@ -53,12 +53,7 @@ struct LoginView: View {
VStack(spacing: AppSpacing.lg) {
// App Icon with gradient
ZStack {
Circle()
.fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 100, height: 100)
.shadow(color: Color.appPrimary.opacity(0.3), radius: 20, y: 10)
Image(systemName: "house.fill")
Image("icon")
.font(.system(size: 50, weight: .semibold))
.foregroundStyle(.white)
}

View File

@@ -13,7 +13,7 @@ struct MainTabView: View {
}
.id(refreshID)
.tabItem {
Label("Residences", systemImage: "house.fill")
Label("Residences", image: "tab_view_house")
}
.tag(0)
.accessibilityIdentifier(AccessibilityIdentifiers.Navigation.residencesTab)

View File

@@ -152,8 +152,16 @@ struct NotificationPreferencesView: View {
.foregroundColor(Color.appTextSecondary)
}
} icon: {
Image(systemName: "house.fill")
.foregroundColor(Color.appPrimary)
Image("house_outline")
.resizable()
.frame(width: 22, height: 22)
.foregroundColor(Color.appTextOnPrimary)
.background(content: {
RoundedRectangle(cornerRadius: AppRadius.sm)
.fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 22, height: 22)
.shadow(color: Color.appPrimary.opacity(0.3), radius: 6, y: 3)
})
}
}
.tint(Color.appPrimary)

View File

@@ -14,26 +14,26 @@ struct ProfileTabView: View {
var body: some View {
List {
Section {
HStack {
Image(systemName: "person.circle.fill")
.resizable()
.frame(width: 60, height: 60)
.foregroundColor(Color.appPrimary)
VStack(alignment: .leading, spacing: 4) {
Text("User Profile")
.font(.headline)
.foregroundColor(Color.appTextPrimary)
Text("Manage your account")
.font(.caption)
.foregroundColor(Color.appTextSecondary)
}
}
.padding(.vertical, 8)
.listRowBackground(Color.appBackgroundSecondary)
}
// Section {
// HStack {
// Image(systemName: "person.circle.fill")
// .resizable()
// .frame(width: 60, height: 60)
// .foregroundColor(Color.appPrimary)
//
// VStack(alignment: .leading, spacing: 4) {
// Text("User Profile")
// .font(.headline)
// .foregroundColor(Color.appTextPrimary)
//
// Text("Manage your account")
// .font(.caption)
// .foregroundColor(Color.appTextSecondary)
// }
// }
// .padding(.vertical, 8)
// .listRowBackground(Color.appBackgroundSecondary)
// }
Section("Account") {
Button(action: {

View File

@@ -3,7 +3,7 @@ import SwiftUI
struct LoginHeader: View {
var body: some View {
VStack(spacing: 8) {
Image(systemName: "house.fill")
Image("icon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 80, height: 80)

View File

@@ -29,7 +29,7 @@ struct OverviewCard: View {
// Stats Grid
HStack(spacing: AppSpacing.md) {
StatView(
icon: "house.fill",
icon: "house_outline",
value: "\(summary.totalResidences)",
label: "Properties",
color: Color.appPrimary

View File

@@ -13,9 +13,22 @@ struct StatView: View {
.fill(color.opacity(0.1))
.frame(width: 48, height: 48)
Image(systemName: icon)
.font(.system(size: 22, weight: .semibold))
.foregroundColor(color)
if icon == "house_outline" {
Image("house_outline")
.resizable()
.frame(width: 22, height: 22)
.foregroundColor(Color.appTextOnPrimary)
.background(content: {
RoundedRectangle(cornerRadius: AppRadius.sm)
.fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 22, height: 22)
.shadow(color: Color.appPrimary.opacity(0.3), radius: 6, y: 3)
})
} else {
Image(systemName: icon)
.font(.system(size: 22, weight: .semibold))
.foregroundColor(color)
}
}
Text(value)

View File

@@ -7,9 +7,20 @@ struct PropertyHeaderCard: View {
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "house.fill")
.font(.title2)
.foregroundColor(Color.appPrimary)
VStack {
Image("house_outline")
.resizable()
.frame(width: 38, height: 38)
.foregroundColor(Color.appTextOnPrimary)
.background(content: {
RoundedRectangle(cornerRadius: AppRadius.sm)
.fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 38, height: 38)
.shadow(color: Color.appPrimary.opacity(0.3), radius: 6, y: 3)
})
Spacer()
}
VStack(alignment: .leading, spacing: 4) {
Text(residence.name)

View File

@@ -3,29 +3,33 @@ import ComposeApp
struct ResidenceCard: View {
let residence: ResidenceResponse
var body: some View {
VStack(alignment: .leading, spacing: AppSpacing.md) {
// Header with property type icon
HStack(spacing: AppSpacing.sm) {
ZStack {
RoundedRectangle(cornerRadius: AppRadius.sm)
.fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
VStack {
Image("house_outline")
.resizable()
.frame(width: 44, height: 44)
.shadow(color: Color.appPrimary.opacity(0.3), radius: 6, y: 3)
Image(systemName: "house.fill")
.font(.system(size: 20, weight: .semibold))
.foregroundColor(Color.appTextOnPrimary)
.background(content: {
RoundedRectangle(cornerRadius: AppRadius.sm)
.fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 44, height: 44)
.shadow(color: Color.appPrimary.opacity(0.3), radius: 6, y: 3)
})
.padding([.trailing], AppSpacing.md)
Spacer()
}
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text(residence.name)
.font(.title3.weight(.semibold))
.fontWeight(.bold)
.foregroundColor(Color.appTextPrimary)
// .lineLimit(1)
if let propertyTypeName = residence.propertyTypeName {
Text(propertyTypeName)
.font(.caption.weight(.medium))
@@ -34,21 +38,26 @@ struct ResidenceCard: View {
.tracking(0.5)
}
}
Spacer()
if residence.isPrimary {
ZStack {
Circle()
.fill(Color.appAccent.opacity(0.2))
.frame(width: 32, height: 32)
VStack {
Image(systemName: "star.fill")
.font(.system(size: 14, weight: .bold))
.foregroundColor(Color.appAccent)
.background(content: {
Circle()
.fill(Color.appAccent.opacity(0.2))
.frame(width: 32, height: 32)
})
.padding(AppSpacing.md)
Spacer()
}
}
}
// Address
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
if !residence.streetAddress.isEmpty {
@@ -61,7 +70,7 @@ struct ResidenceCard: View {
.foregroundColor(Color.appTextSecondary)
}
}
if !residence.city.isEmpty || !residence.stateProvince.isEmpty {
HStack(spacing: AppSpacing.xxs) {
Image(systemName: "location.fill")
@@ -74,13 +83,13 @@ struct ResidenceCard: View {
}
}
.padding(.vertical, AppSpacing.xs)
Divider()
// Fully dynamic task stats from API - show first 3 categories
HStack(spacing: AppSpacing.sm) {
let displayCategories = Array(residence.taskSummary.categories.prefix(3))
ForEach(displayCategories, id: \.name) { category in
TaskStatChip(
icon: category.icons.ios,

View File

@@ -17,7 +17,7 @@ struct SummaryCard: View {
HStack(spacing: 20) {
SummaryStatView(
icon: "house.fill",
icon: "house_outline",
value: "\(summary.totalResidences)",
label: "Properties"
)

View File

@@ -28,7 +28,7 @@ struct SummaryStatView: View {
#Preview {
HStack(spacing: 20) {
SummaryStatView(
icon: "house.fill",
icon: "house_outline",
value: "3",
label: "Properties"
)