mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-17 12:42:15 +01:00
add loading indicator to import (ST-149), change card divider color
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
--theme-color-icon-active: rgb(var(--color-text-tertiary));
|
--theme-color-icon-active: rgb(var(--color-text-tertiary));
|
||||||
--theme-color-card-background: var(--color-bg-secondary);
|
--theme-color-card-background: var(--color-bg-secondary);
|
||||||
--theme-color-card-background-active: var(--color-bg-tertiary);
|
--theme-color-card-background-active: var(--color-bg-tertiary);
|
||||||
--theme-color-card-background-separator: var(--color-border-quaternary);
|
--theme-color-card-background-separator: var(--color-border-tertiary);
|
||||||
--theme-color-card-border: var(--color-border-secondary);
|
--theme-color-card-border: var(--color-border-secondary);
|
||||||
--theme-color-card-border-active: var(--color-border-tertiary);
|
--theme-color-card-border-active: var(--color-border-tertiary);
|
||||||
--theme-color-default-background-separator: var(--color-border-primary);
|
--theme-color-default-background-separator: var(--color-border-primary);
|
||||||
|
|||||||
31
resources/js/Components/LoadingSpinner.vue
Normal file
31
resources/js/Components/LoadingSpinner.vue
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
:class="
|
||||||
|
twMerge('animate-spin -ml-1 mr-3 h-5 w-5 text-white', props.class)
|
||||||
|
"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<circle
|
||||||
|
class="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="4"></circle>
|
||||||
|
<path
|
||||||
|
class="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { HtmlButtonType } from '@/types/dom';
|
import type { HtmlButtonType } from '@/types/dom';
|
||||||
|
import LoadingSpinner from '@/Components/LoadingSpinner.vue';
|
||||||
|
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
type: HtmlButtonType;
|
type: HtmlButtonType;
|
||||||
|
loading: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
|
loading: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
@@ -14,7 +17,9 @@ withDefaults(
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
:type="type"
|
:type="type"
|
||||||
|
:disabled="loading"
|
||||||
class="inline-flex items-center px-2 sm:px-3 py-1 sm:py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-xs sm:text-sm text-white hover:bg-accent-300/20 active:bg-accent-300/20 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
|
class="inline-flex items-center px-2 sm:px-3 py-1 sm:py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-xs sm:text-sm text-white hover:bg-accent-300/20 active:bg-accent-300/20 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
|
||||||
|
<LoadingSpinner v-if="loading"></LoadingSpinner>
|
||||||
<slot />
|
<slot />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ import type { ImportReport, ImportType } from '@/utils/api';
|
|||||||
import DialogModal from '@/Components/DialogModal.vue';
|
import DialogModal from '@/Components/DialogModal.vue';
|
||||||
import SecondaryButton from '@/Components/SecondaryButton.vue';
|
import SecondaryButton from '@/Components/SecondaryButton.vue';
|
||||||
import { initializeStores } from '@/utils/init';
|
import { initializeStores } from '@/utils/init';
|
||||||
|
|
||||||
const importTypeOptions = ref<ImportType[]>([]);
|
const importTypeOptions = ref<ImportType[]>([]);
|
||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const organizationId = getCurrentOrganizationId();
|
const organizationId = getCurrentOrganizationId();
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
@@ -50,28 +53,32 @@ async function importData() {
|
|||||||
const organizationId = getCurrentOrganizationId();
|
const organizationId = getCurrentOrganizationId();
|
||||||
if (organizationId !== null) {
|
if (organizationId !== null) {
|
||||||
const { handleApiRequestNotifications } = useNotificationsStore();
|
const { handleApiRequestNotifications } = useNotificationsStore();
|
||||||
|
loading.value = true;
|
||||||
reportResult.value = await handleApiRequestNotifications(() => {
|
try {
|
||||||
if (importType.value) {
|
reportResult.value = await handleApiRequestNotifications(() => {
|
||||||
return api.importData(
|
if (importType.value) {
|
||||||
{
|
return api.importData(
|
||||||
type: importType.value.key,
|
{
|
||||||
data: base64String,
|
type: importType.value.key,
|
||||||
},
|
data: base64String,
|
||||||
{
|
|
||||||
params: {
|
|
||||||
organization: organizationId,
|
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
);
|
params: {
|
||||||
}
|
organization: organizationId,
|
||||||
return new Promise((resolve, reject) => {
|
},
|
||||||
reject('Import type is null');
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
reject('Import type is null');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
initializeStores();
|
||||||
initializeStores();
|
if (reportResult.value) {
|
||||||
if (reportResult.value) {
|
showResultModal.value = true;
|
||||||
showResultModal.value = true;
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,7 +232,9 @@ const showResultModal = ref(false);
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<PrimaryButton @click="importData">Import Data</PrimaryButton>
|
<PrimaryButton :loading @click="importData"
|
||||||
|
>Import Data</PrimaryButton
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import SecondaryButton from '@/Components/SecondaryButton.vue';
|
|||||||
import { PlusIcon } from '@heroicons/vue/16/solid';
|
import { PlusIcon } from '@heroicons/vue/16/solid';
|
||||||
import TimeEntryCreateModal from '@/Components/Common/TimeEntry/TimeEntryCreateModal.vue';
|
import TimeEntryCreateModal from '@/Components/Common/TimeEntry/TimeEntryCreateModal.vue';
|
||||||
import TimeEntryAggregateRow from '@/Components/Common/TimeEntry/TimeEntryAggregateRow.vue';
|
import TimeEntryAggregateRow from '@/Components/Common/TimeEntry/TimeEntryAggregateRow.vue';
|
||||||
|
import LoadingSpinner from '@/Components/LoadingSpinner.vue';
|
||||||
|
|
||||||
const timeEntriesStore = useTimeEntriesStore();
|
const timeEntriesStore = useTimeEntriesStore();
|
||||||
const { timeEntries, allTimeEntriesLoaded } = storeToRefs(timeEntriesStore);
|
const { timeEntries, allTimeEntriesLoaded } = storeToRefs(timeEntriesStore);
|
||||||
@@ -148,23 +149,7 @@ const showManualTimeEntryModal = ref(false);
|
|||||||
<div
|
<div
|
||||||
v-if="loading && !allTimeEntriesLoaded"
|
v-if="loading && !allTimeEntriesLoaded"
|
||||||
class="flex justify-center items-center py-5 text-white font-medium">
|
class="flex justify-center items-center py-5 text-white font-medium">
|
||||||
<svg
|
<LoadingSpinner></LoadingSpinner>
|
||||||
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<circle
|
|
||||||
class="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="4"></circle>
|
|
||||||
<path
|
|
||||||
class="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
||||||
</svg>
|
|
||||||
<span> Loading more time entries... </span>
|
<span> Loading more time entries... </span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user