diff --git a/app/Http/Controllers/V1/Admin/General/NotesController.php b/app/Http/Controllers/V1/Admin/General/NotesController.php
index cef1d905..88c1110e 100644
--- a/app/Http/Controllers/V1/Admin/General/NotesController.php
+++ b/app/Http/Controllers/V1/Admin/General/NotesController.php
@@ -41,6 +41,15 @@ class NotesController extends Controller
$note = Note::create($request->getNotesPayload());
+ if ($note->is_default) {
+ Note::where('id', '!=', $note->id)
+ ->where('type', $note->type)
+ ->where('is_default', true)
+ ->update([
+ 'is_default' => false,
+ ]);
+ }
+
return new NoteResource($note);
}
@@ -68,6 +77,15 @@ class NotesController extends Controller
$note->update($request->getNotesPayload());
+ if ($note->is_default) {
+ Note::where('id', '!=', $note->id)
+ ->where('type', $note->type)
+ ->where('is_default', true)
+ ->update([
+ 'is_default' => false,
+ ]);
+ }
+
return new NoteResource($note);
}
diff --git a/app/Http/Requests/NotesRequest.php b/app/Http/Requests/NotesRequest.php
index a1c16be9..9dc54ffe 100644
--- a/app/Http/Requests/NotesRequest.php
+++ b/app/Http/Requests/NotesRequest.php
@@ -33,6 +33,9 @@ class NotesRequest extends FormRequest
'notes' => [
'required',
],
+ 'is_default' => [
+ 'required',
+ ],
];
if ($this->isMethod('PUT')) {
diff --git a/app/Http/Resources/NoteResource.php b/app/Http/Resources/NoteResource.php
index 337863c3..1619d333 100644
--- a/app/Http/Resources/NoteResource.php
+++ b/app/Http/Resources/NoteResource.php
@@ -18,6 +18,7 @@ class NoteResource extends JsonResource
'type' => $this->type,
'name' => $this->name,
'notes' => $this->notes,
+ 'is_default' => $this->is_default,
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
diff --git a/database/factories/NoteFactory.php b/database/factories/NoteFactory.php
index 8a557781..3637faae 100644
--- a/database/factories/NoteFactory.php
+++ b/database/factories/NoteFactory.php
@@ -25,6 +25,7 @@ class NoteFactory extends Factory
'name' => $this->faker->word(),
'notes' => $this->faker->text(),
'company_id' => User::find(1)->companies()->first()->id,
+ 'is_default' => $this->faker->boolean(),
];
}
}
diff --git a/database/migrations/2025_01_05_211404_add_is_default_to_notes_table.php b/database/migrations/2025_01_05_211404_add_is_default_to_notes_table.php
new file mode 100644
index 00000000..5c0201f8
--- /dev/null
+++ b/database/migrations/2025_01_05_211404_add_is_default_to_notes_table.php
@@ -0,0 +1,28 @@
+boolean('is_default')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('notes', function (Blueprint $table) {
+ $table->dropColumn('is_default');
+ });
+ }
+};
diff --git a/lang/de.json b/lang/de.json
index e3abf3dc..bb25d8de 100644
--- a/lang/de.json
+++ b/lang/de.json
@@ -1087,6 +1087,8 @@
"description": "Sparen Sie Zeit, indem Sie Notizen erstellen und diese auf Ihren Rechnungen, Angeboten und Zahlungen wiederverwenden.",
"notes": "Hinweise",
"type": "Art",
+ "is_default": "Standardmäßig auswählen",
+ "is_default_description": "Diese Notiz wird standardmäßig in neuen Rechnungen ausgewählt.",
"add_note": "Notiz hinzufügen",
"add_new_note": "Neue Notiz hinzufügen",
"name": "Name",
diff --git a/lang/en.json b/lang/en.json
index 3a0510e7..0d4caaa0 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -1087,6 +1087,8 @@
"description": "Save time by creating notes and reusing them on your invoices, estimates & payments.",
"notes": "Notes",
"type": "Type",
+ "is_default": "Select by default",
+ "is_default_description": "This note will be selected by default in new invoices.",
"add_note": "Add Note",
"add_new_note": "Add New Note",
"name": "Name",
diff --git a/resources/scripts/admin/components/modal-components/NoteModal.vue b/resources/scripts/admin/components/modal-components/NoteModal.vue
index 36881d45..4e5d81bf 100644
--- a/resources/scripts/admin/components/modal-components/NoteModal.vue
+++ b/resources/scripts/admin/components/modal-components/NoteModal.vue
@@ -45,6 +45,11 @@
class="mt-2"
/>
+
{
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
@@ -554,6 +555,7 @@ export const useEstimateStore = (useWindow = false) => {
const taxTypeStore = useTaxTypeStore()
const route = useRoute()
const userStore = useUserStore()
+ const notesStore = useNotesStore()
this.isFetchingInitialSettings = true
this.newEstimate.selectedCurrency = companyStore.selectedCompanyCurrency
@@ -567,6 +569,8 @@ export const useEstimateStore = (useWindow = false) => {
let editActions = []
if (!isEdit) {
+ await notesStore.fetchNotes()
+ this.newEstimate.notes = notesStore.getDefaultNoteForType('Estimate')?.notes
this.newEstimate.tax_per_item =
companyStore.selectedCompanySettings.tax_per_item
this.newEstimate.sales_tax_type = companyStore.selectedCompanySettings.sales_tax_type
diff --git a/resources/scripts/admin/stores/invoice.js b/resources/scripts/admin/stores/invoice.js
index 7f8a92ce..ba8f8b7d 100644
--- a/resources/scripts/admin/stores/invoice.js
+++ b/resources/scripts/admin/stores/invoice.js
@@ -15,6 +15,7 @@ import { useTaxTypeStore } from './tax-type'
import { useCompanyStore } from './company'
import { useItemStore } from './item'
import { useUserStore } from './user'
+import { useNotesStore } from './note'
export const useInvoiceStore = (useWindow = false) => {
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
@@ -479,6 +480,7 @@ export const useInvoiceStore = (useWindow = false) => {
const taxTypeStore = useTaxTypeStore()
const route = useRoute()
const userStore = useUserStore()
+ const notesStore = useNotesStore()
this.isFetchingInitialSettings = true
@@ -491,8 +493,10 @@ export const useInvoiceStore = (useWindow = false) => {
}
let editActions = []
-
+
if (!isEdit) {
+ await notesStore.fetchNotes()
+ this.newInvoice.notes = notesStore.getDefaultNoteForType('Invoice')?.notes
this.newInvoice.tax_per_item =
companyStore.selectedCompanySettings.tax_per_item
this.newInvoice.sales_tax_type = companyStore.selectedCompanySettings.sales_tax_type
diff --git a/resources/scripts/admin/stores/note.js b/resources/scripts/admin/stores/note.js
index f05f2570..724617a9 100644
--- a/resources/scripts/admin/stores/note.js
+++ b/resources/scripts/admin/stores/note.js
@@ -14,6 +14,7 @@ export const useNotesStore = (useWindow = false) => {
currentNote: {
id: null,
type: '',
+ is_default: false,
name: '',
notes: '',
},
@@ -24,9 +25,14 @@ export const useNotesStore = (useWindow = false) => {
},
actions: {
+ getDefaultNoteForType(type) {
+ return this.notes.find((note) => note.type === type && note.is_default)
+ },
+
resetCurrentNote() {
this.currentNote = {
type: '',
+ is_default: false,
name: '',
notes: '',
}
diff --git a/resources/scripts/admin/stores/payment.js b/resources/scripts/admin/stores/payment.js
index b6fb0fb2..2cf1284f 100644
--- a/resources/scripts/admin/stores/payment.js
+++ b/resources/scripts/admin/stores/payment.js
@@ -6,6 +6,7 @@ import { useCompanyStore } from './company'
import { useNotificationStore } from '@/scripts/stores/notification'
import paymentStub from '../stub/payment'
import { handleError } from '@/scripts/helpers/error-handling'
+import { useNotesStore } from './note'
export const usePaymentStore = (useWindow = false) => {
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
@@ -56,6 +57,7 @@ export const usePaymentStore = (useWindow = false) => {
actions: {
fetchPaymentInitialData(isEdit) {
const companyStore = useCompanyStore()
+ const notesStore = useNotesStore()
const route = useRoute()
this.isFetchingInitialData = true
@@ -80,6 +82,8 @@ export const usePaymentStore = (useWindow = false) => {
// On Create
else if (!isEdit && res2.data) {
+ await notesStore.fetchNotes()
+ this.currentPayment.notes = notesStore.getDefaultNoteForType('Payment')?.notes
this.currentPayment.payment_date = moment().format('YYYY-MM-DD')
this.currentPayment.payment_number = res2.data.nextNumber
this.currentPayment.currency =
diff --git a/resources/scripts/admin/stores/recurring-invoice.js b/resources/scripts/admin/stores/recurring-invoice.js
index 8db9cea1..0aa22cce 100644
--- a/resources/scripts/admin/stores/recurring-invoice.js
+++ b/resources/scripts/admin/stores/recurring-invoice.js
@@ -14,6 +14,7 @@ import moment from 'moment'
import _ from 'lodash'
import { useInvoiceStore } from './invoice'
import { useNotificationStore } from '@/scripts/stores/notification'
+import { useNotesStore } from './note'
export const useRecurringInvoiceStore = (useWindow = false) => {
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
@@ -334,6 +335,7 @@ export const useRecurringInvoiceStore = (useWindow = false) => {
const invoiceStore = useInvoiceStore()
const taxTypeStore = useTaxTypeStore()
const route = useRoute()
+ const notesStore = useNotesStore()
this.isFetchingInitialSettings = true
this.newRecurringInvoice.currency = companyStore.selectedCompanyCurrency
@@ -348,6 +350,8 @@ export const useRecurringInvoiceStore = (useWindow = false) => {
// on create
if (!isEdit) {
+ await notesStore.fetchNotes()
+ this.newRecurringInvoice.notes = notesStore.getDefaultNoteForType('Invoice')?.notes
this.newRecurringInvoice.tax_per_item =
companyStore.selectedCompanySettings.tax_per_item
this.newRecurringInvoice.discount_per_item =
diff --git a/resources/scripts/admin/views/invoices/create/InvoiceCreate.vue b/resources/scripts/admin/views/invoices/create/InvoiceCreate.vue
index cc2b0bc4..3a257737 100644
--- a/resources/scripts/admin/views/invoices/create/InvoiceCreate.vue
+++ b/resources/scripts/admin/views/invoices/create/InvoiceCreate.vue
@@ -151,6 +151,7 @@ import { cloneDeep } from 'lodash'
import { useInvoiceStore } from '@/scripts/admin/stores/invoice'
import { useModuleStore } from '@/scripts/admin/stores/module'
+import { useNotesStore } from '@/scripts/admin/stores/note'
import { useCompanyStore } from '@/scripts/admin/stores/company'
import { useCustomFieldStore } from '@/scripts/admin/stores/custom-field'
@@ -169,6 +170,8 @@ const invoiceStore = useInvoiceStore()
const companyStore = useCompanyStore()
const customFieldStore = useCustomFieldStore()
const moduleStore = useModuleStore()
+const notesStore = useNotesStore()
+
const { t } = useI18n()
let route = useRoute()
let router = useRouter()
diff --git a/resources/scripts/admin/views/settings/NotesSetting.vue b/resources/scripts/admin/views/settings/NotesSetting.vue
index 320e3d5b..4b4c934d 100644
--- a/resources/scripts/admin/views/settings/NotesSetting.vue
+++ b/resources/scripts/admin/views/settings/NotesSetting.vue
@@ -31,6 +31,10 @@
:load-data="refreshTable"
/>
+
+ {{ row.data.name }}
+
+
{{ getLabelNote(row.data.type) }}
@@ -42,9 +46,7 @@
import { computed, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useModalStore } from '@/scripts/stores/modal'
-import { useDialogStore } from '@/scripts/stores/dialog'
import { useNotesStore } from '@/scripts/admin/stores/note'
-import { useNotificationStore } from '@/scripts/stores/notification'
import NoteDropdown from '@/scripts/admin/components/dropdowns/NoteIndexDropdown.vue'
import NoteModal from '@/scripts/admin/components/modal-components/NoteModal.vue'
import { useUserStore } from '@/scripts/admin/stores/user'
@@ -53,9 +55,7 @@ import abilities from '@/scripts/admin/stub/abilities'
const { t } = useI18n()
const modalStore = useModalStore()
-const dialogStore = useDialogStore()
const noteStore = useNotesStore()
-const notificationStore = useNotificationStore()
const userStore = useUserStore()
const table = ref('')
@@ -66,7 +66,7 @@ const notesColumns = computed(() => {
key: 'name',
label: t('settings.customization.notes.name'),
thClass: 'extra',
- tdClass: 'font-medium text-gray-900',
+ tdClass: 'font-medium text-gray-900 flex gap-1 items-center',
},
{
key: 'type',
diff --git a/tailwind.config.js b/tailwind.config.js
index dfe331a5..49e0ae57 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,5 +1,5 @@
-const colors = require('tailwindcss/colors')
-const svgToDataUri = require('mini-svg-data-uri')
+import { red, teal, slate } from 'tailwindcss/colors';
+import svgToDataUri from 'mini-svg-data-uri';
function withOpacityValue(cssVariable) {
return ({ opacityVariable, opacityValue }) => {
@@ -13,11 +13,13 @@ function withOpacityValue(cssVariable) {
};
}
-module.exports = {
- content: [
- './resources/views/**/*.php',
- './resources/scripts/**/*.js',
- './resources/scripts/**/*.vue',
+export default {
+ plugins: [
+ require('@tailwindcss/forms'),
+ require('@tailwindcss/typography'),
+ require('@tailwindcss/aspect-ratio'),
+ require('tailwind-scrollbar'),
+ require('@rvxlab/tailwind-plugin-ios-full-height')
],
theme: {
extend: {
@@ -35,9 +37,9 @@ module.exports = {
900: withOpacityValue('--color-primary-900'),
},
black: '#040405',
- red: colors.red,
- teal: colors.teal,
- gray: colors.slate,
+ red: red,
+ teal: teal,
+ gray: slate,
},
spacing: {
88: '22rem',
@@ -45,8 +47,8 @@ module.exports = {
backgroundImage: (theme) => ({
'multiselect-caret': `url("${svgToDataUri(
``
+
+ `
)}")`,
'multiselect-spinner': `url("${svgToDataUri(
`