Skip to content

Commit

Permalink
Merge pull request #930 from CleverCloud/new-smart-design/cc-invoice-…
Browse files Browse the repository at this point in the history
…list

refactor(cc-invoice-list)!: migrate to the new smart component design
  • Loading branch information
pdesoyres-cc authored Feb 2, 2024
2 parents f033704 + 488d928 commit ffc7464
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 118 deletions.
6 changes: 3 additions & 3 deletions demo-smart/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@
<li>
<a class="definition-link" href="?definition=cc-grafana-info.smart">cc-grafana-info.smart</a>
</li>
<!-- <li>-->
<!-- <a class="definition-link" href="?definition=cc-invoice-list.smart">cc-invoice-list.smart</a>-->
<!-- </li>-->
<li>
<a class="definition-link" href="?definition=cc-invoice-list.smart">cc-invoice-list.smart</a>
</li>
<li>
<a class="definition-link" href="?definition=cc-invoice.smart">cc-invoice.smart</a>
</li>
Expand Down
150 changes: 80 additions & 70 deletions src/components/cc-invoice-list/cc-invoice-list.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { css, html, LitElement } from 'lit';
import '../cc-block/cc-block.js';
import '../cc-block-section/cc-block-section.js';
import '../cc-button/cc-button.js';
import '../cc-notice/cc-notice.js';
import '../cc-select/cc-select.js';
import '../cc-toggle/cc-toggle.js';
import '../cc-block/cc-block.js';
import { css, html, LitElement } from 'lit';
import { ResizeController } from '../../controllers/resize-controller.js';
import { i18n } from '../../lib/i18n.js';
import '../cc-notice/cc-notice.js';
import '../cc-block-section/cc-block-section.js';
import { sortBy, unique } from '../../lib/utils.js';
import { PENDING_STATUSES, PROCESSED_STATUSES, PROCESSING_STATUS } from '../cc-invoice-table/cc-invoice-table.js';

Expand All @@ -25,7 +25,7 @@ function maxFromStrings (strings) {
}

/**
* @typedef {import('../common.types.js').Invoice} Invoice
* @typedef {import('./cc-invoice-list.types.js').InvoiceListState} InvoiceListState
*/

/**
Expand All @@ -37,20 +37,16 @@ export class CcInvoiceList extends LitElement {

static get properties () {
return {
error: { type: Boolean },
invoices: { type: Array },
state: { type: Object },
_yearFilter: { type: Number, state: true },
};
}

constructor () {
super();

/** @type {boolean} Sets a loading error state. */
this.error = false;

/** @type {Invoice[]|null} Sets the list of invoices. */
this.invoices = null;
/** @type {InvoiceListState} Sets the invoices state. */
this.state = { type: 'loading' };

/** @type {number|null} */
this._yearFilter = null;
Expand All @@ -65,12 +61,29 @@ export class CcInvoiceList extends LitElement {
}

render () {

const skeleton = (this.invoices == null);

const pendingInvoices = skeleton ? [] : this.invoices.filter((i) => PENDING_STATUSES.includes(i.status));
const processingInvoices = skeleton ? [] : this.invoices.filter((i) => i.status === PROCESSING_STATUS);
const processedInvoices = skeleton ? [] : this.invoices.filter((i) => PROCESSED_STATUSES.includes(i.status));
if (this.state.type === 'error') {
return this._renderView(html`
<cc-notice intent="warning" message="${i18n('cc-invoice-list.error')}"></cc-notice>
`);
}

if (this.state.type === 'loading') {
return this._renderView(html`
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.pending')}</div>
<cc-invoice-table></cc-invoice-table>
</cc-block-section>
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.processed')}</div>
<cc-invoice-table></cc-invoice-table>
</cc-block-section>
`);
}

const pendingInvoices = this.state.invoices.filter((i) => PENDING_STATUSES.includes(i.status));
const processingInvoices = this.state.invoices.filter((i) => i.status === PROCESSING_STATUS);
const processedInvoices = this.state.invoices.filter((i) => PROCESSED_STATUSES.includes(i.status));

const processedInvoicesYears = processedInvoices
.map((invoice) => getYearAsString(invoice.emissionDate))
Expand All @@ -86,62 +99,59 @@ export class CcInvoiceList extends LitElement {

const filteredProcessedInvoices = processedInvoices.filter((i) => getYearAsString(i.emissionDate) === yearFilter);

const hasYearSelector = !skeleton && filteredProcessedInvoices.length > 0 && yearChoices.length > 1;
const hasYearSelector = filteredProcessedInvoices.length > 0 && yearChoices.length > 1;

return this._renderView(html`
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.pending')}</div>
${pendingInvoices.length > 0 ? html`
<cc-invoice-table .invoices=${pendingInvoices}></cc-invoice-table>
` : ''}
${pendingInvoices.length === 0 ? html`
<div class="empty-msg">${i18n('cc-invoice-list.pending.no-invoices')}</div>
` : ''}
</cc-block-section>
${processingInvoices.length > 0 ? html`
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.processing')}</div>
<cc-invoice-table .invoices=${processingInvoices}></cc-invoice-table>
</cc-block-section>
` : ''}
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.processed')}</div>
${hasYearSelector ? html`
<cc-toggle
legend=${i18n('cc-invoice-list.year')}
.choices=${yearChoices}
value=${yearFilter}
inline
@cc-toggle:input=${this._onYearFilterValue}
></cc-toggle>
<cc-select
label=${i18n('cc-invoice-list.year')}
.options=${yearChoices}
value=${yearFilter}
inline
@cc-select:input=${this._onYearFilterValue}
></cc-select>
` : ''}
${filteredProcessedInvoices.length > 0 ? html`
<cc-invoice-table .invoices=${filteredProcessedInvoices}></cc-invoice-table>
` : ''}
${filteredProcessedInvoices.length === 0 ? html`
<div class="empty-msg ">${i18n('cc-invoice-list.processed.no-invoices')}</div>
` : ''}
</cc-block-section>
`);
}

_renderView (content) {
return html`
<cc-block>
<div slot="title">${i18n('cc-invoice-list.title')}</div>
${!this.error ? html`
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.pending')}</div>
${skeleton || pendingInvoices.length > 0 ? html`
<cc-invoice-table .invoices=${skeleton ? null : pendingInvoices}></cc-invoice-table>
` : ''}
${!skeleton && pendingInvoices.length === 0 ? html`
<div class="empty-msg">${i18n('cc-invoice-list.pending.no-invoices')}</div>
` : ''}
</cc-block-section>
${!skeleton && processingInvoices.length > 0 ? html`
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.processing')}</div>
<cc-invoice-table .invoices=${skeleton ? null : processingInvoices}></cc-invoice-table>
</cc-block-section>
` : ''}
<cc-block-section>
<div slot="title">${i18n('cc-invoice-list.processed')}</div>
${hasYearSelector ? html`
<cc-toggle
legend=${i18n('cc-invoice-list.year')}
.choices=${yearChoices}
value=${yearFilter}
inline
@cc-toggle:input=${this._onYearFilterValue}
></cc-toggle>
<cc-select
label=${i18n('cc-invoice-list.year')}
.options=${yearChoices}
value=${yearFilter}
inline
@cc-select:input=${this._onYearFilterValue}
></cc-select>
` : ''}
${skeleton || filteredProcessedInvoices.length > 0 ? html`
<cc-invoice-table .invoices=${skeleton ? null : filteredProcessedInvoices}></cc-invoice-table>
` : ''}
${!skeleton && filteredProcessedInvoices.length === 0 ? html`
<div class="empty-msg ">${i18n('cc-invoice-list.processed.no-invoices')}</div>
` : ''}
</cc-block-section>
` : ''}
${this.error ? html`
<cc-notice intent="warning" message="${i18n('cc-invoice-list.error')}"></cc-notice>
` : ''}
${content}
</cc-block>
`;
}
Expand Down
34 changes: 13 additions & 21 deletions src/components/cc-invoice-list/cc-invoice-list.smart.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
import './cc-invoice-list.js';
import '../cc-smart-container/cc-smart-container.js';
import { fetchAllInvoices } from '../../lib/api-helpers.js';
import { defineSmartComponentWithObservables } from '../../lib/define-smart-component-with-observables.js';
import { LastPromise, unsubscribeWithSignal } from '../../lib/observables.js';
import { defineSmartComponent } from '../../lib/define-smart-component.js';

defineSmartComponentWithObservables({
defineSmartComponent({
selector: 'cc-invoice-list',
params: {
apiConfig: { type: Object },
ownerId: { type: String },
},
onConnect (container, component, context$, disconnectSignal) {
onContextUpdate ({ context, updateComponent, signal }) {

const invoices_lp = new LastPromise();
updateComponent('state', { type: 'loading' });

unsubscribeWithSignal(disconnectSignal, [
const { apiConfig, ownerId } = context;

invoices_lp.error$.subscribe(console.error),
invoices_lp.error$.subscribe(() => (component.error = true)),
invoices_lp.value$.subscribe((invoices) => (component.invoices = invoices)),

context$.subscribe(({ apiConfig, ownerId }) => {

component.eror = null;
component.invoices = null;

if (apiConfig != null && ownerId != null) {
invoices_lp.push((signal) => fetchAllInvoices({ apiConfig, signal, ownerId }));
}
}),

]);
fetchAllInvoices({ apiConfig, ownerId, signal })
.then((invoices) => {
updateComponent('state', { type: 'loaded', invoices });
})
.catch((error) => {
console.error(error);
updateComponent('state', { type: 'error' });
});
},
});
58 changes: 34 additions & 24 deletions src/components/cc-invoice-list/cc-invoice-list.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,63 +25,73 @@ const conf = {
};

export const defaultStory = makeStory(conf, {
items: [{ invoices: fullInvoicesExample }],
items: [{ state: { type: 'loaded', invoices: fullInvoicesExample } }],
});

export const skeleton = makeStory(conf, {
export const loading = makeStory(conf, {
items: [{}],
});

export const empty = makeStory(conf, {
items: [{ invoices: [] }],
items: [{ state: { type: 'loaded', invoices: [] } }],
});

export const error = makeStory(conf, {
items: [{ error: true }],
items: [{ state: { type: 'error' } }],
});

export const dataLoaded = makeStory(conf, {
items: [{ invoices: fullInvoicesExample }],
items: [{ state: { type: 'loaded', invoices: fullInvoicesExample } }],
});

export const dataLoadedWithNoProcessing = makeStory(conf, {
items: [{ invoices: fullInvoicesExample.filter((i) => i.status !== PROCESSING_STATUS) }],
items: [{ state: { type: 'loaded', invoices: fullInvoicesExample.filter((i) => i.status !== PROCESSING_STATUS) } }],
});

export const dataLoadedWithNoPending = makeStory(conf, {
items: [{
invoices: [
...processedInvoices('2020'),
...processedInvoices('2020').slice(2, 10),
...processedInvoices('2018').slice(2, 10),
...processedInvoices('2017').slice(3, 11),
...processedInvoices('2016').slice(1, 9),
...processedInvoices('2015').slice(1, 9),
],
}],
items: [
{
state:
{
type: 'loaded',
invoices: [
...processedInvoices('2020'),
...processedInvoices('2020').slice(2, 10),
...processedInvoices('2018').slice(2, 10),
...processedInvoices('2017').slice(3, 11),
...processedInvoices('2016').slice(1, 9),
...processedInvoices('2015').slice(1, 9),
],
},
}],
});

export const dataLoadedWithNoProcessed = makeStory(conf, {
items: [{
invoices: [
...pendingInvoices(2020).slice(0, 4),
],
}],
items: [
{
state:
{
type: 'loaded',
invoices: [
...pendingInvoices(2020).slice(0, 4),
],
},
}],
});

export const simulations = makeStory(conf, {
items: [{}, {}],
simulations: [
storyWait(2000, ([component, componentError]) => {
component.invoices = fullInvoicesExample;
componentError.error = true;
component.state = { type: 'loaded', invoices: fullInvoicesExample };
componentError.state = { type: 'error' };
}),
],
});

enhanceStoriesNames({
defaultStory,
skeleton,
loading,
empty,
error,
dataLoaded,
Expand Down
16 changes: 16 additions & 0 deletions src/components/cc-invoice-list/cc-invoice-list.types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Invoice } from "../common.types";

export type InvoiceListState = InvoiceListStateLoading | InvoiceListStateError | InvoiceListStateLoaded;

interface InvoiceListStateLoading {
type: 'loading';
}

interface InvoiceListStateError {
type: 'error';
}

interface InvoiceListStateLoaded {
type: 'loaded';
invoices: Array<Invoice>;
}

0 comments on commit ffc7464

Please sign in to comment.