Skip to content

Commit

Permalink
Merge pull request #599 from evershopcommerce/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
treoden authored Aug 21, 2024
2 parents dce90e1 + db9a569 commit e4a4314
Show file tree
Hide file tree
Showing 130 changed files with 4,629 additions and 211 deletions.
254 changes: 242 additions & 12 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions packages/evershop/bin/build/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
process.env.ALLOW_CONFIG_MUTATIONS = true;
const config = require('config');
const { existsSync, rmSync, mkdirSync } = require('fs');
const path = require('path');
const { CONSTANTS } = require('@evershop/evershop/src/lib/helpers');
Expand All @@ -11,8 +13,14 @@ const {
const { buildEntry } = require('@evershop/evershop/bin/lib/buildEntry');
const { getCoreModules } = require('@evershop/evershop/bin/lib/loadModules');
const { error } = require('@evershop/evershop/src/lib/log/logger');
const { lockHooks } = require('@evershop/evershop/src/lib/util/hookable');
const { lockRegistry } = require('@evershop/evershop/src/lib/util/registry');
const {
validateConfiguration
} = require('@evershop/evershop/src/lib/util/validateConfiguration');
const { compile } = require('./complie');
const { getEnabledExtensions } = require('../extension');
const { loadBootstrapScript } = require('../lib/bootstrap/bootstrap');
require('dotenv').config();
/* Loading modules and initilize routes, components */
const modules = [...getCoreModules(), ...getEnabledExtensions()];
Expand All @@ -38,6 +46,22 @@ if (existsSync(path.resolve(CONSTANTS.BUILDPATH))) {
}

(async () => {
/** Loading bootstrap script from modules */
try {
// eslint-disable-next-line no-restricted-syntax
for (const module of modules) {
await loadBootstrapScript(module);
}
lockHooks();
lockRegistry();
// Get the configuration (nodeconfig)
validateConfiguration(config);
} catch (e) {
error(e);
process.exit(0);
}
process.env.ALLOW_CONFIG_MUTATIONS = false;

const routes = getRoutes();
await buildEntry(routes.filter((r) => isBuildRequired(r)));

Expand Down
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
15 changes: 15 additions & 0 deletions packages/evershop/bin/lib/buildEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ const {
} = require('@evershop/evershop/src/lib/webpack/util/parseGraphql');
const JSON5 = require('json5');
const { error } = require('@evershop/evershop/src/lib/log/logger');
const {
getEnabledWidgets
} = require('@evershop/evershop/src/lib/util/getEnabledWidgets');
/**
* Only pass the page routes, not api routes
*/
module.exports.buildEntry = async function buildEntry(
routes,
clientOnly = false
) {
const widgets = getEnabledWidgets();
await Promise.all(
routes.map(async (route) => {
const subPath = getRouteBuildPath(route);
Expand Down Expand Up @@ -70,6 +74,16 @@ module.exports.buildEntry = async function buildEntry(
route.isAdmin ? 'HydrateAdmin' : 'HydrateFrontStore'
}';
`;
areas['*'] = areas['*'] || {};
widgets.forEach((widget) => {
areas['*'][widget.type] = {
id: widget.type,
sortOrder: widget.sortOrder || 0,
component: route.isAdmin
? `---require('${widget.setting_component}')---`
: `---require('${widget.component}')---`
};
});
contentClient += '\r\n';
contentClient += `Area.defaultProps.components = ${inspect(areas, {
depth: 5
Expand All @@ -93,6 +107,7 @@ module.exports.buildEntry = async function buildEntry(
/** Build query */
const query = `${JSON.stringify(parseGraphql(components))}`;

// Loop through the widgets config and add the query to the widgets
let contentServer = `import React from 'react'; `;
contentServer += '\r\n';
contentServer += `import ReactDOM from 'react-dom'; `;
Expand Down
8 changes: 8 additions & 0 deletions packages/evershop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
"@babel/preset-react": "^7.16.0",
"@ckeditor/ckeditor5-build-classic": "^36.0.1",
"@ckeditor/ckeditor5-react": "^5.1.0",
"@editorjs/editorjs": "^2.30.2",
"@editorjs/header": "^2.8.7",
"@editorjs/list": "^1.10.0",
"@editorjs/quote": "^2.6.0",
"@editorjs/raw": "^2.5.0",
"@evershop/editorjs-image": "^1.0.0",
"@evershop/postgres-query-builder": "^1.2.0",
"@graphql-tools/load-files": "^6.6.1",
"@graphql-tools/merge": "^8.3.5",
Expand All @@ -50,6 +56,7 @@
"@shopify/draggable": "^1.0.0-beta.8",
"@stripe/react-stripe-js": "^1.5.0",
"@stripe/stripe-js": "^1.18.0",
"@tailwindcss/typography": "^0.5.13",
"ajv": "^8.12.0",
"ajv-errors": "^3.0.0",
"ajv-formats": "^2.1.1",
Expand Down Expand Up @@ -82,6 +89,7 @@
"html-webpack-plugin": "^5.5.0",
"immer": "^9.0.3",
"is-object": "^1.0.2",
"is-resolvable": "^1.1.0",
"jsesc": "^3.0.2",
"json5": "^2.2.1",
"kleur": "3.0.3",
Expand Down
28 changes: 28 additions & 0 deletions packages/evershop/src/components/admin/cms/widget/WidgetTypes.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function WidgetTypes({ types }) {
return (
<div className="grid grid-cols-3 gap-4">
{types.map((type) => (
<a
key={type.code}
href={type.createWidgetUrl}
className="border border-gray-200 rounded p-4 text-center"
>
<div className="text-lg font-bold">{type.name}</div>
</a>
))}
</div>
);
}

WidgetTypes.propTypes = {
types: PropTypes.arrayOf(
PropTypes.shape({
code: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
createWidgetUrl: PropTypes.string.isRequired
})
).isRequired
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import PropTypes from 'prop-types';
import React from 'react';

export default function WidgetTypeRow({ code, types }) {
const type = types.find((t) => t.code === code);
if (!type) {
return (
<td>
<div>Unknown</div>
</td>
);
} else {
return (
<td>
<div>{type.name}</div>
</td>
);
}
}

WidgetTypeRow.propTypes = {
code: PropTypes.string.isRequired,
types: PropTypes.arrayOf(
PropTypes.shape({
code: PropTypes.string.isRequired,
name: PropTypes.string.isRequired
})
).isRequired
};
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")
}`;
Loading

0 comments on commit e4a4314

Please sign in to comment.