Skip to content

Commit

Permalink
Multi config support for picker (#23)
Browse files Browse the repository at this point in the history
* Multi config support for picker

* Multi config support for picker
  • Loading branch information
herzog31 authored Jan 31, 2024
1 parent c6bba24 commit fc88fa3
Show file tree
Hide file tree
Showing 6 changed files with 684 additions and 529 deletions.
1 change: 0 additions & 1 deletion tools/picker/dist/index.4fd1a205.js.map

This file was deleted.

1,009 changes: 506 additions & 503 deletions tools/picker/dist/index.4fd1a205.js → tools/picker/dist/index.ebd701ed.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tools/picker/dist/index.ebd701ed.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tools/picker/dist/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Franklin Picker</title><link rel="stylesheet" href="index.96de328f.css"></head><body> <div id="app"></div> <script type="module" src="index.4fd1a205.js"></script> </body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Franklin Picker</title><link rel="stylesheet" href="index.96de328f.css"></head><body> <div id="app"></div> <script type="module" src="index.ebd701ed.js"></script> </body></html>
35 changes: 20 additions & 15 deletions tools/picker/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import getProductsInCategory from './queries/products.graphql.js';

import './styles.css';

const endpoint = 'https://catalog-service-sandbox.adobe.io/graphql';
const rootCategoryKey = '2';
const configFile = ' https://main--aem-boilerplate-commerce--hlxsites.hlx.live/configs.json';
const defaultConfig = 'prod';

/**
* List of blocks to be available in the picker.
Expand Down Expand Up @@ -100,18 +100,18 @@ const blocks = {
},
};

async function performCatalogServiceQuery(query, variables) {
async function performCatalogServiceQuery(query, config, variables) {
const headers = {
'Magento-Environment-Id': '3b0cc8f4-7dd3-4bcb-bb65-b9e11358a080',
'Magento-Store-View-Code': 'default',
'Magento-Website-Code': 'base',
'x-api-key': 'ba7fa6fca7a64922a533e8c28426aa44',
'Magento-Store-Code': 'main_website_store',
'Magento-Customer-Group': 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c',
'Magento-Environment-Id': config['commerce-environment-id'],
'Magento-Store-View-Code': config['commerce-store-view-code'],
'Magento-Website-Code': config['commerce-website-code'],
'x-api-key': config['commerce-x-api-key'],
'Magento-Store-Code': config['commerce-store-code'],
'Magento-Customer-Group': config['commerce-customer-group'],
'Content-Type': 'application/json',
};

const apiCall = new URL(endpoint);
const apiCall = new URL(config['commerce-endpoint']);
apiCall.searchParams.append('query', query.replace(/(?:\r\n|\r|\n|\t|[\s]{4})/g, ' ')
.replace(/\s\s+/g, ' '));
apiCall.searchParams.append('variables', variables ? JSON.stringify(variables) : null);
Expand All @@ -130,11 +130,11 @@ async function performCatalogServiceQuery(query, variables) {
return queryResponse.data;
}

const getItems = async (folderKey, page = 1) => {
const getItems = async (folderKey, page = 1, config) => {
let newItems = {};
let pageInfo = {};
try {
const products = await performCatalogServiceQuery(getProductsInCategory, { id: folderKey, currentPage: page });
const products = await performCatalogServiceQuery(getProductsInCategory, config, { id: folderKey, currentPage: page });
products?.productSearch?.items.forEach(product => {
const { productView } = product;

Expand All @@ -158,11 +158,11 @@ const getItems = async (folderKey, page = 1) => {
return [newItems, pageInfo];
};

const getCategories = async (folderKey) => {
const getCategories = async (folderKey, config) => {
let categoryObject = {};

try {
const categories = await performCatalogServiceQuery(getCategoriesInCategory, { id: folderKey });
const categories = await performCatalogServiceQuery(getCategoriesInCategory, config, { id: folderKey });
categories?.categories.forEach(category => {
categoryObject[category.id] = category;
});
Expand All @@ -175,5 +175,10 @@ const getCategories = async (folderKey) => {

const app = document.getElementById("app");
if (app) {
ReactDOM.render(<Picker blocks={blocks} getCategories={getCategories} getItems={getItems} rootCategoryKey={rootCategoryKey} />, app);
ReactDOM.render(<Picker
blocks={blocks}
getCategories={getCategories}
getItems={getItems}
configFile={configFile}
defaultConfig={defaultConfig} />, app);
}
165 changes: 156 additions & 9 deletions tools/picker/src/picker.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import React, { useEffect, useState } from 'react';

import { defaultTheme, Provider, ListView, Item, Text, Image, Heading, Breadcrumbs, ActionButton, Flex, Picker as RSPicker, View, IllustratedMessage } from '@adobe/react-spectrum';
import { defaultTheme, Provider, ListView, Item, Text, Image, Heading, Content, Breadcrumbs, ActionButton, Flex, Picker as RSPicker, View, IllustratedMessage } from '@adobe/react-spectrum';
import Folder from '@spectrum-icons/illustrations/Folder';
import NotFound from '@spectrum-icons/illustrations/NotFound';
import Error from '@spectrum-icons/illustrations/Error';
import Copy from '@spectrum-icons/workflow/Copy';
import Settings from '@spectrum-icons/workflow/Settings';


const Picker = props => {
const { blocks, getItems, getCategories, rootCategoryKey } = props;
const { blocks, getItems, getCategories, configFile, defaultConfig } = props;

const [state, setState] = useState({
items: {},
folder: rootCategoryKey,
configs: {},
selectedConfig: null,
folder: null,
path: [],
categories: {},
loadingState: 'loading',
block: null,
disabledKeys: new Set(),
selectedItems: new Set(),
showSettings: false,
error: null,
pageInfo: {
current_page: 1,
page_size: 0,
total_pages: 0,
},
});

const activeConfig = state.selectedConfig ? state.configs[state.selectedConfig] : null;

const clickListItem = (key) => {
const block = blocks[state.block] || {};
if (!key.startsWith('category:') || block?.selection === 'multiple') {
Expand Down Expand Up @@ -118,8 +127,16 @@ const Picker = props => {
</IllustratedMessage>
);

const renderErrorState = () => (
<IllustratedMessage>
<Error />
<Heading>Something went wrong</Heading>
<Content>{state.error}</Content>
</IllustratedMessage>
);

const onLoadMore = async () => {
if (state.pageInfo.current_page >= state.pageInfo.total_pages || state.loadingState === 'loading') {
if (!state.pageInfo || state.pageInfo.current_page >= state.pageInfo.total_pages || state.loadingState === 'loading') {
return;
}

Expand All @@ -128,7 +145,7 @@ const Picker = props => {
loadingState: 'loading',
}));

const [items, pageInfo] = await getItems(state.folder, state.pageInfo.current_page + 1);
const [items, pageInfo] = await getItems(state.folder, state.pageInfo?.current_page + 1, activeConfig);
Object.values(items).forEach(i => {
i.key = i.sku;
});
Expand All @@ -148,9 +165,101 @@ const Picker = props => {
});
}

const toggleSettings = () => {
setState(state => ({
...state,
showSettings: !state.showSettings,
}));
}

const changeSelectedConfig = (config) => {
setState(state => ({
...state,
selectedConfig: config,
folder: state.configs[config]['commerce-root-category-id'],
path: [],
categories: {},
loadingState: 'loading',
disabledKeys: new Set(),
selectedItems: new Set(),
pageInfo: {
current_page: 1,
page_size: 0,
total_pages: 0,
},
}));
}

useEffect(() => {
(async () => {
const categories = await getCategories(rootCategoryKey);
// Get configs and select default config
let configs = {};
try {
configs = await fetch(configFile).then(r => r.json());
} catch (err) {
console.error(err);
setState(state => ({
...state,
error: 'Could not load config file',
}));
return;
}
// Ignore metadata
Object.keys(configs).forEach(key => {
if (key.startsWith(':')) {
delete configs[key];
}
});

// Flatten values
Object.keys(configs).forEach(key => {
const values = {};
configs[key].data.forEach(e => {
values[e.key] = e.value;
});
configs[key] = values;
});

const selectedConfig = defaultConfig || Object.keys(configs)[0];
const rootCategoryKey = configs[selectedConfig]['commerce-root-category-id'];

setState(state => ({
...state,
configs,
selectedConfig,
folder: rootCategoryKey,
path: [],
categories: {},
loadingState: 'loading',
disabledKeys: new Set(),
selectedItems: new Set(),
pageInfo: {
current_page: 1,
page_size: 0,
total_pages: 0,
},
}));
})();
}, []);

useEffect(() => {
(async () => {
if (!activeConfig) {
return;
}

let categories = {};
try {
categories = await getCategories(activeConfig['commerce-root-category-id'], activeConfig);
} catch(err) {
console.error(err);
setState(state => ({
...state,
error: 'Could not load categories',
}));
return;
}

Object.values(categories).forEach(c => {
c.key = `category:${c.id}`;
c.isFolder = true;
Expand All @@ -165,11 +274,27 @@ const Picker = props => {
}
});
})();
}, []);
}, [state.selectedConfig])

useEffect(() => {
(async () => {
let [items, pageInfo] = await getItems(state.folder);
if (!activeConfig) {
return;
}

let items = {};
let pageInfo = {};
try {
([items, pageInfo]) = await getItems(state.folder, 1, activeConfig);
} catch(err) {
console.error(err);
setState(state => ({
...state,
error: 'Could not load items',
}));
return;
}

Object.values(items).forEach(i => {
i.key = i.sku;
});
Expand All @@ -189,15 +314,37 @@ const Picker = props => {
}
});
})();
}, [state.folder]);
}, [state.selectedConfig, state.folder]);

const currentBlock = blocks[state.block] || {};
const items = [...getCategoriesToDisplay(state.categories), ...Object.values(state.items)];

if (state.error) {
return <Provider theme={defaultTheme} height="100%">
<Flex direction="column" height="100%">
<View padding="size-500">
{renderErrorState()}
</View>
</Flex>
</Provider>;
}

return <Provider theme={defaultTheme} height="100%">
<Flex direction="column" height="100%">
{state.showSettings && <View padding="size-100">
<RSPicker label="Configuration"
isRequired
width="100%"
selectedKey={state.selectedConfig}
onSelectionChange={key => changeSelectedConfig(key)}>
{Object.keys(state.configs).map(key => (
<Item key={key} value={key}>{key}</Item>
))}
</RSPicker>
</View>}
<View padding="size-100">
<Flex direction="row" gap="size-100">
<ActionButton aria-label="Settings" isQuiet onPress={toggleSettings}><Settings /></ActionButton>
<RSPicker width="100%"
items={Object.values(blocks)}
aria-label="Select a block"
Expand Down

0 comments on commit fc88fa3

Please sign in to comment.