Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions assets/scss/_onboarding.scss
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@

.primary-btn {
background: $primary;
color: white;
color: $text-inverse;

&:hover {
background: $primary-hover;
Expand Down Expand Up @@ -206,7 +206,7 @@
svg {
width: 40px;
height: 40px;
color: white;
color: $text-inverse;
}
}

Expand Down
3 changes: 3 additions & 0 deletions assets/scss/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
--color-text-primary: #333333;
--color-text-secondary: #374151;
--color-text-muted: #6b7280;
--color-text-inverse: #ffffff;

// Background colors
--color-bg-light: #f8faf9;
Expand Down Expand Up @@ -78,6 +79,7 @@
--color-text-primary: #f9fafb;
--color-text-secondary: #e5e7eb;
--color-text-muted: #9ca3af;
--color-text-inverse: #ffffff;

// Background colors - darker hierarchy for better contrast
--color-bg-light: #0f172a;
Expand Down Expand Up @@ -124,6 +126,7 @@ $info: var(--color-info);
$text-primary: var(--color-text-primary);
$text-secondary: var(--color-text-secondary);
$text-muted: var(--color-text-muted);
$text-inverse: var(--color-text-inverse);

// Background colors
$bg-light: var(--color-bg-light);
Expand Down
19 changes: 17 additions & 2 deletions components/FormSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<button
class="tab-button tab-button--expense"
:class="{ active: isExpenseSelected }"
:disabled="props.isSubmitting"
@click="selectExpense"
>
<ArrowUpTrayIcon class="tab-icon" />
Expand All @@ -12,6 +13,7 @@
<button
class="tab-button tab-button--income"
:class="{ active: isIncomeSelected }"
:disabled="props.isSubmitting"
@click="selectIncome"
>
<ArrowDownTrayIcon class="tab-icon" />
Expand All @@ -20,6 +22,7 @@
<button
class="tab-button tab-button--transfer"
:class="{ active: isTransferSelected }"
:disabled="props.isSubmitting"
@click="selectTransfer"
>
<ArrowsRightLeftIcon class="tab-icon" />
Expand All @@ -32,9 +35,14 @@
v-if="!isTransferSelected"
:is-outcome-selected="isExpenseSelected"
:editing-item="props.editingItem"
:is-submitting="props.isSubmitting"
@submit="handleTransactionSubmit"
/>
<TransferFormContainer v-else @submit="handleTransferSubmit" />
<TransferFormContainer
v-else
:is-submitting="props.isSubmitting"
@submit="handleTransferSubmit"
/>
</div>
</div>
</template>
Expand All @@ -48,7 +56,8 @@ import TransferFormContainer from './TransferFormContainer.vue';
const { t } = useI18n();

const props = defineProps({
editingItem: { type: Object, default: null }
editingItem: { type: Object, default: null },
isSubmitting: { type: Boolean, default: false }
});

const emit = defineEmits(['submit', 'transfer']);
Expand Down Expand Up @@ -153,6 +162,12 @@ watch(
color: $primary;
}

&:disabled {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none;
}

&.active {
color: $primary;
background: $primary-light;
Expand Down
69 changes: 35 additions & 34 deletions components/PartiesForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
v-model="form.name"
type="text"
class="form-input"
:class="{ error: nameError }"
:class="{ error: nameError || props.apiError }"
:placeholder="t('Enter party name')"
/>
<div v-if="nameError" class="error-text">{{ t('Party name is required.') }}</div>
<div v-if="props.apiError" class="error-text">{{ props.apiError }}</div>
</div>
<div class="icon-col">
<button
Expand Down Expand Up @@ -86,15 +87,15 @@
<button type="button" class="btn btn-secondary" @click="handleClose">
{{ t('Cancel') }}
</button>
<button type="submit" class="btn btn-primary">
<button type="submit" class="btn btn-primary" :disabled="props.isSubmitting">
{{ isEditing ? t('Update Party') : t('Create Party') }}
</button>
</div>
</form>
</template>

<script setup>
import { ref, nextTick, computed, watch } from 'vue';
import { ref, computed, watch } from 'vue';
import IconPicker from './IconPicker.vue';
import * as lucideIcons from 'lucide-vue-next';
import { ImagePlus, X } from 'lucide-vue-next';
Expand All @@ -105,6 +106,14 @@ const props = defineProps({
editingItem: {
type: Object,
default: null
},
apiError: {
type: String,
default: ''
},
isSubmitting: {
type: Boolean,
default: false
}
});

Expand Down Expand Up @@ -207,44 +216,36 @@ function validateForm() {
return isValid;
}

async function handleSubmit() {
function handleSubmit() {
if (props.isSubmitting) {
return;
}

// Validate all fields
if (!validateForm()) {
return;
}

try {
const formData = {
name: form.value.name.trim(),
type: form.value.type,
description: form.value.description.trim()
};
if (form.value.icon && form.value.icon.trim() !== '') {
formData.icon = form.value.icon;
formData.icon_type = 'image';
}

if (isEditing.value) {
const updatedItem = {
id: props.editingItem.id,
...formData
};
emit('updated', updatedItem);
} else {
emit('created', formData);
}

// give the parent a tick to handle the event (avoid race)
await nextTick();
const formData = {
name: form.value.name.trim(),
type: form.value.type,
description: form.value.description.trim()
};
if (form.value.icon && form.value.icon.trim() !== '') {
formData.icon = form.value.icon;
formData.icon_type = 'image';
}

// now close
emit('close');
} catch (err) {
console.error('Error submitting form:', err);
} finally {
// reset form and errors
resetForm();
if (isEditing.value) {
const updatedItem = {
id: props.editingItem.id,
...formData
};
emit('updated', updatedItem);
return;
}

emit('created', formData);
}

function onIconSelected() {
Expand Down
83 changes: 67 additions & 16 deletions components/TSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,62 +36,85 @@
<li>
<NuxtLink
to="/transactions"
class="nav-button"
class="nav-button nav-button--with-subtext"
active-class="selected"
@click="handleNavClick"
>
<RectangleGroupIcon class="icon" />
<span class="text">{{ t('Transactions') }}</span>
<div class="nav-copy">
<span class="text">{{ t('Transactions') }}</span>
<span class="subtext">{{ t('All money movements in one place') }}</span>
</div>
</NuxtLink>
</li>
<li>
<NuxtLink
to="/categories"
class="nav-button"
class="nav-button nav-button--with-subtext"
active-class="selected"
@click="handleNavClick"
>
<BuildingLibraryIcon class="icon" />
<span class="text">{{ t('Categories') }}</span>
<div class="nav-copy">
<span class="text">{{ t('Categories') }}</span>
<span class="subtext">{{ t('Organize transactions by type') }}</span>
</div>
</NuxtLink>
</li>
<li>
<NuxtLink to="/groups" class="nav-button" active-class="selected" @click="handleNavClick">
<NuxtLink
to="/groups"
class="nav-button nav-button--with-subtext"
active-class="selected"
@click="handleNavClick"
>
<UserGroupIcon class="icon" />
<span class="text">{{ t('Groups') }}</span>
<div class="nav-copy">
<span class="text">{{ t('Groups') }}</span>
<span class="subtext">{{ t('Bundle related transactions') }}</span>
</div>
</NuxtLink>
</li>
<li>
<NuxtLink
to="/parties"
class="nav-button"
class="nav-button nav-button--with-subtext"
active-class="selected"
@click="handleNavClick"
>
<UserGroupIcon class="icon" />
<span class="text">{{ t('Parties') }}</span>
<div class="nav-copy">
<span class="text">{{ t('Parties') }}</span>
<span class="subtext">{{ t('People and organizations you transact with') }}</span>
</div>
</NuxtLink>
</li>
<li>
<NuxtLink
to="/wallets"
class="nav-button"
class="nav-button nav-button--with-subtext"
active-class="selected"
@click="handleNavClick"
>
<WalletIcon class="icon" />
<span class="text">{{ t('Wallets') }}</span>
<div class="nav-copy">
<span class="text">{{ t('Wallets') }}</span>
<span class="subtext">{{ t('Accounts money moves through') }}</span>
</div>
</NuxtLink>
</li>
<li>
<NuxtLink
to="/reminders"
class="nav-button"
class="nav-button nav-button--with-subtext"
active-class="selected"
@click="handleNavClick"
>
<BellIcon class="icon" />
<span class="text">{{ t('Reminders') }}</span>
<div class="nav-copy">
<span class="text">{{ t('Reminders') }}</span>
<span class="subtext">{{ t('Track upcoming or expected transactions') }}</span>
</div>
</NuxtLink>
</li>
</ul>
Expand Down Expand Up @@ -241,9 +264,9 @@ const handleButtonNavClick = (path) => {
display: flex;
align-items: center;
width: calc(100% - 20px);
height: 56px;
min-height: 56px;
border-radius: $radius-xl;
padding: 16px 8px;
padding: 12px 10px;
gap: 12px;
background-color: transparent;
border: none;
Expand All @@ -252,21 +275,49 @@ const handleButtonNavClick = (path) => {
cursor: pointer;
transition: all 0.3s ease;
margin-left: 10px;
position: relative;

&:hover:not(.selected) {
background-color: rgba(var(--color-primary-rgb), 0.15);
margin: 2px 10px;
height: 52px;
}

&.selected {
background-color: $primary-light;

.subtext {
color: $text-secondary;
}
}
}

.nav-button--with-subtext {
align-items: flex-start;
}

.nav-copy {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}

.text {
font-size: $font-size-sm;
line-height: 1.2;
color: inherit;
}

.subtext {
font-size: $font-size-xs;
line-height: 1.35;
color: $text-muted;
}

.icon {
width: 24px;
height: 24px;
flex-shrink: 0;
margin-top: 2px;
}

.close-button {
Expand Down
Loading