Skip to content

Commit

Permalink
feat: improving new project creations (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasz97 authored Oct 11, 2024
1 parent 4e712e7 commit acbdef4
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 129 deletions.
32 changes: 27 additions & 5 deletions src/components/form/NewProjectForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ interface NewProjectFormProp {
alertMessageState: ModalState;
onCancel$?: QRL;
allowNewEntry?: boolean;
preSelectedData: Signal<{
customer?: string;
project?: string;
}>;
}

export const NewProjectForm = component$<NewProjectFormProp>(
({ timeEntry, alertMessageState, onCancel$, allowNewEntry }) => {
({ timeEntry, alertMessageState, onCancel$, allowNewEntry, preSelectedData }) => {
const {
dataCustomersSig,
dataProjectsSig,
Expand Down Expand Up @@ -61,7 +65,9 @@ export const NewProjectForm = component$<NewProjectFormProp>(
onCancel$ && onCancel$();
});

const _projectSelected = useSignal(projectSelected.value.name);
const _projectSelected = useSignal(
preSelectedData.value.project ?? projectSelected.value.name
);
const _taskSelected = useSignal(taskSelected.value.name);

const _projectOptions = useComputed$(() => {
Expand Down Expand Up @@ -102,16 +108,32 @@ export const NewProjectForm = component$<NewProjectFormProp>(
useTask$(({ track }) => {
track(() => projectSelected.value);
_projectSelected.value = projectSelected.value.name;
_projectTypeSelected.value = projectSelected.value.type;
});

useTask$(({ track }) => {
track(() => taskSelected.value);
_taskSelected.value = taskSelected.value.name;
});

useVisibleTask$(({ track }) => {
track(() => projectSelected.value);
_projectTypeSelected.value = projectSelected.value.type;
useVisibleTask$(async ({ track }) => {
track(() => preSelectedData.value);

if (
preSelectedData.value.customer === undefined &&
preSelectedData.value.project === undefined
) {
return;
}

if (preSelectedData.value.customer) {
customerSelected.value = preSelectedData.value.customer;
await onChangeCustomer(preSelectedData.value.customer);
}

if (preSelectedData.value.project) {
await _onChangeProject(preSelectedData.value.project);
}
});

return (
Expand Down
31 changes: 29 additions & 2 deletions src/components/modals/NewTimeEntryModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import { $, component$, Slot, useComputed$, useSignal } from '@builder.io/qwik';
import {
$,
component$,
Signal,
Slot,
useComputed$,
useSignal,
useVisibleTask$,
} from '@builder.io/qwik';
import { t } from 'src/locale/labels';
import { getIcon } from '../icons';

export const NewTimeEntryModal = component$(() => {
interface NewTimeEntryModalProp {
preSelectedData?: Signal<{
customer?: string;
project?: string;
}>;
}

export const NewTimeEntryModal = component$<NewTimeEntryModalProp>(({ preSelectedData }) => {
const modalVisible = useSignal(false);

const modalToggle = $(() => {
Expand All @@ -15,6 +30,18 @@ export const NewTimeEntryModal = component$(() => {
};
});

useVisibleTask$(({ track }) => {
track(() => preSelectedData?.value);

if (
preSelectedData &&
(preSelectedData.value.customer !== undefined ||
preSelectedData.value.project !== undefined)
) {
modalVisible.value = true;
}
});

return (
<>
<div class='flex w-full flex-row'>
Expand Down
254 changes: 152 additions & 102 deletions src/components/registry/CustomerAccordion.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { $, component$, QRL, useSignal, useStore } from '@builder.io/qwik';
import {
$,
component$,
QRL,
Signal,
sync$,
useSignal,
useStore,
useVisibleTask$,
} from '@builder.io/qwik';
import { Customer } from '@models/customer';
import { ModalState } from '@models/modalState';
import { useCustomers } from 'src/hooks/useCustomers';
Expand All @@ -16,112 +25,153 @@ import { ProjectAccordion } from './ProjectAccordion';
interface CustomerAccordionProps {
customer: Customer;
refresh?: QRL;
preSelectedData: Signal<{
customer?: string;
project?: string;
}>;
preOpenData: Signal<{
customer?: string;
project?: string;
beenOpened?: boolean;
}>;
}

export const CustomerAccordion = component$<CustomerAccordionProps>(({ customer, refresh }) => {
const { addEvent } = useNotification();
const { updateCustomer, removeCustomer } = useCustomers();
const { projects, fetchProjects, isLoading } = useProjects();
const visibleBody = useSignal(false);

const name = useSignal(customer);

const initFormSignals = $(() => {
name.value = customer;
});

const customerModalState = useStore<ModalState>({
title: t('EDIT_CUSTOMER'),
onCancel$: $(() => {
initFormSignals();
}),
onConfirm$: $(async () => {
if (await updateCustomer(customer, name.value)) {
refresh && refresh();
addEvent({
type: 'success',
message: t('EDIT_CUSTOMER_SUCCESS_MESSAGE'),
autoclose: true,
});
}
}),
cancelLabel: t('ACTION_CANCEL'),
confirmLabel: t('ACTION_CONFIRM'),
});

const customerDeleteModalState = useStore<ModalState>({
title: t('CUSTOMER_DELETE_TITLE'),
message: tt('CUSTOMER_DELETE_MESSAGE', {
name: customer,
}),
onConfirm$: $(async () => {
if (await removeCustomer(customer)) {
refresh && refresh();
addEvent({
type: 'success',
message: t('DELETE_CUSTOMER_SUCCESS_MESSAGE'),
autoclose: true,
});
export const CustomerAccordion = component$<CustomerAccordionProps>(
({ customer, refresh, preSelectedData, preOpenData }) => {
const { addEvent } = useNotification();
const { updateCustomer, removeCustomer } = useCustomers();
const { projects, fetchProjects, isLoading } = useProjects();
const visibleBody = useSignal(false);

const name = useSignal(customer);

const initFormSignals = $(() => {
name.value = customer;
});

const customerModalState = useStore<ModalState>({
title: t('EDIT_CUSTOMER'),
onCancel$: $(() => {
initFormSignals();
}),
onConfirm$: $(async () => {
if (await updateCustomer(customer, name.value)) {
refresh && refresh();
addEvent({
type: 'success',
message: t('EDIT_CUSTOMER_SUCCESS_MESSAGE'),
autoclose: true,
});
}
}),
cancelLabel: t('ACTION_CANCEL'),
confirmLabel: t('ACTION_CONFIRM'),
});

const customerDeleteModalState = useStore<ModalState>({
title: t('CUSTOMER_DELETE_TITLE'),
message: tt('CUSTOMER_DELETE_MESSAGE', {
name: customer,
}),
onConfirm$: $(async () => {
if (await removeCustomer(customer)) {
refresh && refresh();
addEvent({
type: 'success',
message: t('DELETE_CUSTOMER_SUCCESS_MESSAGE'),
autoclose: true,
});
}
}),
cancelLabel: t('ACTION_CANCEL'),
confirmLabel: t('ACTION_CONFIRM'),
});

const openBody = sync$(async () => {
visibleBody.value = !visibleBody.value;
if (visibleBody.value) await fetchProjects(customer);
});

const selectPreselected = $(() => {
preSelectedData.value = {
customer: customer,
project: undefined,
};
});

useVisibleTask$(async () => {
if (preOpenData.value.customer === customer && !preOpenData.value.beenOpened) {
await openBody().then(() =>
preOpenData.value.project === undefined
? (preOpenData.value.beenOpened = true)
: null
);
}
}),
cancelLabel: t('ACTION_CANCEL'),
confirmLabel: t('ACTION_CONFIRM'),
});

const openBody = $(() => {
visibleBody.value = !visibleBody.value;
if (visibleBody.value) fetchProjects(customer);
});

return (
<>
<h2>
<div class='rfocus:ring-4 flex w-full items-center justify-between gap-3 border-gray-200 p-5 font-medium text-gray-500 hover:bg-gray-100 focus:ring-gray-200 rtl:text-right'>
<div class='flex flex-row gap-3'>
<span>{customer}</span> {isLoading.value && <LoadingSpinnerInline />}
</div>
<div class='flex flex-row gap-3'>
<Button
variant={'outline'}
onClick$={() => (customerDeleteModalState.isVisible = true)}
>
{getIcon('Bin')}
</Button>

<Button
variant={'outline'}
onClick$={() => (customerModalState.isVisible = true)}
>
{getIcon('Edit')}
</Button>

<AccordionOpenButton onClick$={openBody} accordionState={visibleBody} />
});

return (
<>
<h2>
<div
class={
'flex w-full items-center justify-between gap-3 border-gray-200 p-5 font-medium text-gray-500 hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 rtl:text-right ' +
(visibleBody.value && !isLoading.value
? 'border-2 bg-dark-gray-50'
: '')
}
>
<div class='flex flex-row gap-3'>
<span>{customer}</span> {isLoading.value && <LoadingSpinnerInline />}
</div>
<div class='flex flex-row gap-3'>
<Button
variant={'outline'}
onClick$={() => (customerDeleteModalState.isVisible = true)}
>
{getIcon('Bin')}
</Button>

<Button
variant={'outline'}
onClick$={() => (customerModalState.isVisible = true)}
>
{getIcon('Edit')}
</Button>

<Button variant={'outline'} onClick$={selectPreselected}>
{getIcon('Add')}
</Button>

<AccordionOpenButton onClick$={openBody} accordionState={visibleBody} />
</div>
</div>
</div>
</h2>

{/* accordion body */}
<div class={visibleBody.value && !isLoading.value ? '' : 'hidden'}>
<div class='border border-gray-200 p-5'>
<div id='accordion-nested-collapse' data-accordion='collapse'>
{projects.value.map((project) => {
return (
<ProjectAccordion
customer={customer}
project={project}
refresh={refresh}
/>
);
})}
</h2>

{/* accordion body */}
<div class={visibleBody.value && !isLoading.value ? '' : 'hidden'}>
<div class='border border-gray-200 p-5'>
<div id='accordion-nested-collapse' data-accordion='collapse'>
{projects.value.map((project) => {
return (
<ProjectAccordion
customer={customer}
project={project}
refresh={refresh}
preSelectedData={preSelectedData}
preOpenData={preOpenData}
/>
);
})}
</div>
</div>
</div>
</div>

<Modal state={customerModalState}>
<EditCustomerForm name={name} />
</Modal>
<Modal state={customerModalState}>
<EditCustomerForm name={name} />
</Modal>

<Modal state={customerDeleteModalState} />
</>
);
});
<Modal state={customerDeleteModalState} />
</>
);
}
);
Loading

0 comments on commit acbdef4

Please sign in to comment.