Skip to content

Commit

Permalink
Widget feature #581
Browse files Browse the repository at this point in the history
  • Loading branch information
treoden committed Aug 21, 2024
1 parent 00e9952 commit 1cae4cb
Show file tree
Hide file tree
Showing 37 changed files with 826 additions and 175 deletions.
5 changes: 5 additions & 0 deletions packages/evershop/bin/lib/addDefaultMiddlewareFuncs.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ exports.addDefaultMiddlewareFuncs = function addDefaultMiddlewareFuncs(
} else {
middlewareFunc = route.webpackMiddleware;
}
middlewareFunc.waitUntilValid(() => {
const { stats } = middlewareFunc.context;
const jsonWebpackStats = stats.toJson();
response.locals.jsonWebpackStats = jsonWebpackStats;
});
// We need to run build for notFound route
const notFoundRoute = routes.find((r) => r.id === 'notFound');
const notFoundWebpackCompiler = notFoundRoute.webpackCompiler;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { Card } from '@components/admin/cms/Card';
import Spinner from '@components/common/Spinner';
import PropTypes from 'prop-types';
import React from 'react';
import { useQuery } from 'urql';
import CheckIcon from '@heroicons/react/outline/CheckIcon';
import { SimplePageination } from '@components/common/SimplePagination';
import { Field } from '@components/common/form/Field';

const SearchQuery = `
query Query ($filters: [FilterInput!]) {
collections(filters: $filters) {
items {
collectionId
uuid
code
name
}
total
}
}
`;

function CollectionProductsSetting({
collectionProductsWidget: { collection, count }
}) {
const limit = 10;
const [inputValue, setInputValue] = React.useState(null);
const [selectedCollection, setSelectedCollection] =
React.useState(collection);
const [page, setPage] = React.useState(1);

const [result, reexecuteQuery] = useQuery({
query: SearchQuery,
variables: {
filters: inputValue
? [
{ key: 'name', operation: 'like', value: inputValue },
{ key: 'page', operation: 'eq', value: page.toString() },
{ key: 'limit', operation: 'eq', value: limit.toString() }
]
: [
{ key: 'limit', operation: 'eq', value: limit.toString() },
{ key: 'page', operation: 'eq', value: page.toString() }
]
},
pause: true
});

React.useEffect(() => {
reexecuteQuery({ requestPolicy: 'network-only' });
}, []);

React.useEffect(() => {
const timer = setTimeout(() => {
if (inputValue !== null) {
reexecuteQuery({ requestPolicy: 'network-only' });
}
}, 1500);

return () => clearTimeout(timer);
}, [inputValue]);

React.useEffect(() => {
reexecuteQuery({ requestPolicy: 'network-only' });
}, [page]);

const { data, fetching, error } = result;

if (error) {
return (
<p>
There was an error fetching collections.
{error.message}
</p>
);
}

return (
<div>
<div className="modal-content">
<Card.Session title="Select a collection">
<div>
<div className="border rounded border-divider mb-8">
<input
type="text"
value={inputValue}
placeholder="Search collections"
onChange={(e) => setInputValue(e.target.value)}
/>
<Field
type="hidden"
name="settings[collection]"
value={selectedCollection}
validationRules={['notEmpty']}
/>
</div>
{fetching && (
<div className="p-3 border border-divider rounded flex justify-center items-center">
<Spinner width={25} height={25} />
</div>
)}
{!fetching && data && (
<div className="divide-y">
{data.collections.items.length === 0 && (
<div className="p-3 border border-divider rounded flex justify-center items-center">
{inputValue ? (
<p>
No collections found for query &quot;{inputValue}&rdquo;
</p>
) : (
<p>You have no collections to display</p>
)}
</div>
)}
{data.collections.items.map((collection) => (
<div
key={collection.uuid}
className="grid grid-cols-8 gap-8 py-4 border-divider items-center"
>
<div className="col-span-6">
<h3>{collection.name}</h3>
</div>
<div className="col-span-2 text-right">
<div className="flex items-center">
{!(collection.code === selectedCollection) && (
<button
type="button"
className="button secondary"
onClick={(e) => {
e.preventDefault();
setSelectedCollection(collection.code);
}}
>
Select
</button>
)}
{collection.code === selectedCollection && (
<CheckIcon width={20} height={20} />
)}
</div>
</div>
</div>
))}
</div>
)}
</div>
</Card.Session>
<Card.Session title="Number of products to display">
<div className="flex justify-between gap-8">
<Field
type="text"
name="settings[count]"
value={count}
validationRules={['notEmpty']}
/>
</div>
</Card.Session>
</div>
<Card.Session>
<div className="flex justify-between gap-8">
<SimplePageination
total={data?.collections.total}
count={data?.collections?.items?.length || 0}
page={page}
hasNext={limit * page < data?.collections.total}
setPage={setPage}
/>
</div>
</Card.Session>
</div>
);
}

CollectionProductsSetting.propTypes = {
collectionProductsWidget: PropTypes.shape({
collection: PropTypes.string,
count: PropTypes.number
})
};

CollectionProductsSetting.defaultProps = {
collectionProductsWidget: {
collection: '',
count: 5
}
};

export default CollectionProductsSetting;

export const query = `
query Query($collection: String, $count: Int) {
collectionProductsWidget(collection: $collection, count: $count) {
collection
count
}
}
`;

export const variables = `{
collection: getWidgetSetting("collection"),
count: getWidgetSetting("count")
}`;
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import Editor from '@components/common/form/fields/Editor';
import { Field } from '@components/common/form/Field';

export default function TextBlockSetting({
text,
textWidget: { text, className },
browserApi,
deleteApi,
uploadApi,
folderCreateApi
}) {
return (
<div>
<Field
type="text"
name="settings[className]"
label="Custom css classes"
value={className}
placeholder="Custom css classes"
/>
<Editor
name="settings[text]"
label="Content"
Expand All @@ -25,13 +33,38 @@ export default function TextBlockSetting({
}

TextBlockSetting.propTypes = {
text: PropTypes.string,
browserApi: PropTypes.string.isRequired,
deleteApi: PropTypes.string.isRequired,
uploadApi: PropTypes.string.isRequired,
folderCreateApi: PropTypes.string.isRequired
folderCreateApi: PropTypes.string.isRequired,
textWidget: PropTypes.shape({
// eslint-disable-next-line react/forbid-prop-types
text: PropTypes.array,
className: PropTypes.string
})
};

TextBlockSetting.defaultProps = {
text: []
textWidget: {
text: [],
className: ''
}
};

export const query = `
query Query($text: String, $className: String) {
textWidget(text: $text, className: $className) {
text
className
}
browserApi: url(routeId: "fileBrowser", params: [{key: "0", value: ""}])
deleteApi: url(routeId: "fileDelete", params: [{key: "0", value: ""}])
uploadApi: url(routeId: "imageUpload", params: [{key: "0", value: ""}])
folderCreateApi: url(routeId: "folderCreate")
}
`;

export const variables = `{
text: getWidgetSetting("text"),
className: getWidgetSetting("className")
}`;
8 changes: 4 additions & 4 deletions packages/evershop/src/components/common/Area.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ function Area(props) {
const assignedWidgets = [];

widgets.forEach((widget) => {
const w = wildCardWidgets[widget.widgetId];
const w = wildCardWidgets[widget.type];
if (widget.areaId.includes(id) && w !== undefined) {
assignedWidgets.push({
id: widget.type,
id: widget.id,
sortOrder: widget.sortOrder,
props: widget.props,
component: w.component
Expand All @@ -41,7 +41,7 @@ function Area(props) {
.concat(assignedWidgets);
return cs.sort((obj1, obj2) => obj1.sortOrder - obj2.sortOrder);
})();

const { propsMap } = context;
let WrapperComponent = React.Fragment;
if (noOuter !== true) {
if (wrapper !== undefined) {
Expand All @@ -66,9 +66,9 @@ function Area(props) {
const C = w.component.default;
// eslint-disable-next-line no-shadow
const { id } = w;
const { propsMap } = context;
const propsData = context.graphqlResponse;
const propKeys = propsMap[id] || [];

const componentProps = propKeys.reduce((acc, map) => {
const { origin, alias } = map;
acc[origin] = propsData[alias];
Expand Down
Loading

0 comments on commit 1cae4cb

Please sign in to comment.