update profile and UI tweaks
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
12
iosApp/iosApp/Assets.xcassets/house_outline.imageset/Contents.json
vendored
Normal file
12
iosApp/iosApp/Assets.xcassets/house_outline.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "house_outline.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
iosApp/iosApp/Assets.xcassets/house_outline.imageset/house_outline.pdf
vendored
Normal file
BIN
iosApp/iosApp/Assets.xcassets/house_outline.imageset/house_outline.pdf
vendored
Normal file
Binary file not shown.
12
iosApp/iosApp/Assets.xcassets/icon.imageset/Contents.json
vendored
Normal file
12
iosApp/iosApp/Assets.xcassets/icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
iosApp/iosApp/Assets.xcassets/icon.imageset/icon.pdf
vendored
Normal file
BIN
iosApp/iosApp/Assets.xcassets/icon.imageset/icon.pdf
vendored
Normal file
Binary file not shown.
26
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/Contents.json
vendored
Normal file
26
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/Contents.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
BIN
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/house_outline 1.png
vendored
Normal file
BIN
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/house_outline 1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
BIN
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/house_outline 2.png
vendored
Normal file
BIN
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/house_outline 2.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/house_outline.png
vendored
Normal file
BIN
iosApp/iosApp/Assets.xcassets/tab_view_house.imageset/house_outline.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -17,7 +17,7 @@ struct SummaryCard: View {
|
||||
|
||||
HStack(spacing: 20) {
|
||||
SummaryStatView(
|
||||
icon: "house.fill",
|
||||
icon: "house_outline",
|
||||
value: "\(summary.totalResidences)",
|
||||
label: "Properties"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ struct SummaryStatView: View {
|
||||
#Preview {
|
||||
HStack(spacing: 20) {
|
||||
SummaryStatView(
|
||||
icon: "house.fill",
|
||||
icon: "house_outline",
|
||||
value: "3",
|
||||
label: "Properties"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user