From 56da108599856285df9060748d8db7571e47d646 Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Sat, 31 May 2025 10:37:56 +0300 Subject: [PATCH 1/2] chore: add kendo-react-redux-undo app --- examples/kendo-react-redux-undo/.gitignore | 27 + examples/kendo-react-redux-undo/LICENSE | 21 + examples/kendo-react-redux-undo/README.md | 13 + examples/kendo-react-redux-undo/package.json | 31 + .../kendo-react-redux-undo/public/favicon.ico | Bin 0 -> 3870 bytes .../kendo-react-redux-undo/public/index.html | 40 + .../public/manifest.json | 15 + examples/kendo-react-redux-undo/src/App.css | 28 + examples/kendo-react-redux-undo/src/App.js | 17 + .../kendo-react-redux-undo/src/App.test.js | 9 + .../src/actions/actions.js | 23 + .../src/components/GridContainer.js | 106 ++ .../src/components/MyCommandCell.js | 17 + .../src/data/products.js | 1233 +++++++++++++++++ examples/kendo-react-redux-undo/src/index.css | 9 + examples/kendo-react-redux-undo/src/index.js | 15 + examples/kendo-react-redux-undo/src/logo.svg | 7 + .../src/reducers/index.js | 7 + .../src/reducers/reducers.js | 70 + .../src/registerServiceWorker.js | 117 ++ 20 files changed, 1805 insertions(+) create mode 100644 examples/kendo-react-redux-undo/.gitignore create mode 100644 examples/kendo-react-redux-undo/LICENSE create mode 100644 examples/kendo-react-redux-undo/README.md create mode 100644 examples/kendo-react-redux-undo/package.json create mode 100644 examples/kendo-react-redux-undo/public/favicon.ico create mode 100644 examples/kendo-react-redux-undo/public/index.html create mode 100644 examples/kendo-react-redux-undo/public/manifest.json create mode 100644 examples/kendo-react-redux-undo/src/App.css create mode 100644 examples/kendo-react-redux-undo/src/App.js create mode 100644 examples/kendo-react-redux-undo/src/App.test.js create mode 100644 examples/kendo-react-redux-undo/src/actions/actions.js create mode 100644 examples/kendo-react-redux-undo/src/components/GridContainer.js create mode 100644 examples/kendo-react-redux-undo/src/components/MyCommandCell.js create mode 100644 examples/kendo-react-redux-undo/src/data/products.js create mode 100644 examples/kendo-react-redux-undo/src/index.css create mode 100644 examples/kendo-react-redux-undo/src/index.js create mode 100644 examples/kendo-react-redux-undo/src/logo.svg create mode 100644 examples/kendo-react-redux-undo/src/reducers/index.js create mode 100644 examples/kendo-react-redux-undo/src/reducers/reducers.js create mode 100644 examples/kendo-react-redux-undo/src/registerServiceWorker.js diff --git a/examples/kendo-react-redux-undo/.gitignore b/examples/kendo-react-redux-undo/.gitignore new file mode 100644 index 00000000..aae8560f --- /dev/null +++ b/examples/kendo-react-redux-undo/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +**/kendo-ui-license** + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# package-lock file +package-lock.json \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/LICENSE b/examples/kendo-react-redux-undo/LICENSE new file mode 100644 index 00000000..01893653 --- /dev/null +++ b/examples/kendo-react-redux-undo/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Telerik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/kendo-react-redux-undo/README.md b/examples/kendo-react-redux-undo/README.md new file mode 100644 index 00000000..7f168c4b --- /dev/null +++ b/examples/kendo-react-redux-undo/README.md @@ -0,0 +1,13 @@ +# kendo-react-redux-undo +Kendo UI Grid using [Redux](https://redux.js.org/) for state management and [Redux Undo](https://github.com/omnidan/redux-undo) for history management. + +## Build Setup + +```bash +# install dependencies +npm install +# serve with hot reload at localhost:3000 +npm start +# build for production with minification +npm run build + diff --git a/examples/kendo-react-redux-undo/package.json b/examples/kendo-react-redux-undo/package.json new file mode 100644 index 00000000..d6eb8426 --- /dev/null +++ b/examples/kendo-react-redux-undo/package.json @@ -0,0 +1,31 @@ +{ + "name": "kendo-redux-timetravel-editing", + "version": "0.1.0", + "private": true, + "dependencies": { + "@progress/kendo-data-query": "^1.5.0", + "@progress/kendo-drawing": "^1.5.7", + "@progress/kendo-react-dateinputs": "^1.2.0", + "@progress/kendo-react-dropdowns": "^1.2.0", + "@progress/kendo-react-grid": "^1.2.0", + "@progress/kendo-react-inputs": "^1.2.0", + "@progress/kendo-react-intl": "^1.2.0", + "@progress/kendo-react-pdf": "^1.2.0", + "@progress/kendo-theme-default": "^2.55.0", + "react": "^16.4.2", + "react-dom": "^16.4.2", + "react-redux": "^5.0.7", + "react-scripts": "1.1.5", + "redux": "^4.0.0", + "redux-devtools-extension": "^2.13.5", + "redux-thunk": "^2.3.0", + "redux-undo": "^0.6.1", + "prop-types": "15.6.2" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} diff --git a/examples/kendo-react-redux-undo/public/favicon.ico b/examples/kendo-react-redux-undo/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/examples/kendo-react-redux-undo/public/index.html b/examples/kendo-react-redux-undo/public/index.html new file mode 100644 index 00000000..ed0ebafa --- /dev/null +++ b/examples/kendo-react-redux-undo/public/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + React App + + + +
+ + + diff --git a/examples/kendo-react-redux-undo/public/manifest.json b/examples/kendo-react-redux-undo/public/manifest.json new file mode 100644 index 00000000..ef19ec24 --- /dev/null +++ b/examples/kendo-react-redux-undo/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": "./index.html", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/kendo-react-redux-undo/src/App.css b/examples/kendo-react-redux-undo/src/App.css new file mode 100644 index 00000000..c5c6e8a6 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/App.css @@ -0,0 +1,28 @@ +.App { + text-align: center; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 80px; +} + +.App-header { + background-color: #222; + height: 150px; + padding: 20px; + color: white; +} + +.App-title { + font-size: 1.5em; +} + +.App-intro { + font-size: large; +} + +@keyframes App-logo-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} diff --git a/examples/kendo-react-redux-undo/src/App.js b/examples/kendo-react-redux-undo/src/App.js new file mode 100644 index 00000000..90b3a7ed --- /dev/null +++ b/examples/kendo-react-redux-undo/src/App.js @@ -0,0 +1,17 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import './App.css'; +import GridContainer from './components/GridContainer'; +import '@progress/kendo-theme-default/dist/all.css'; + +class App extends Component { + render() { + return ( +
+ +
+ ); + } +} + +export default connect()(App); diff --git a/examples/kendo-react-redux-undo/src/App.test.js b/examples/kendo-react-redux-undo/src/App.test.js new file mode 100644 index 00000000..a754b201 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/App.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/examples/kendo-react-redux-undo/src/actions/actions.js b/examples/kendo-react-redux-undo/src/actions/actions.js new file mode 100644 index 00000000..a4d2bba3 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/actions/actions.js @@ -0,0 +1,23 @@ +export const ADD_PRODUCT = 'ADD_PRODUCT' +export const UPDATE_PRODUCT = 'UPDATE_PRODUCT' +export const DELETE_PRODUCT = 'DELETE_PRODUCT' +export const CHANGE_EDIT = 'CHANGE_EDIT' + +export const DATASTATE_CHANGE = 'DATASTATE_CHANGE' + +export function addProduct(payload) { + return { type: ADD_PRODUCT, payload } +} +export function updateProduct(payload) { + return { type: UPDATE_PRODUCT, payload } +} +export function deleteProduct(payload) { + return { type: DELETE_PRODUCT, payload } +} +export function changeEdit(payload) { + return { type: CHANGE_EDIT, payload } +} + +export function datastateChange(payload) { + return { type: DATASTATE_CHANGE, payload } +} diff --git a/examples/kendo-react-redux-undo/src/components/GridContainer.js b/examples/kendo-react-redux-undo/src/components/GridContainer.js new file mode 100644 index 00000000..a9170522 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/components/GridContainer.js @@ -0,0 +1,106 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import {addProduct, updateProduct, deleteProduct, changeEdit, datastateChange} from '../actions/actions'; +import MyCommandCell from './MyCommandCell' +import { ActionCreators as UndoActionCreators } from 'redux-undo'; +import {Grid, GridColumn as Column, GridToolbar} from '@progress/kendo-react-grid'; +import { process } from '@progress/kendo-data-query' + +class GridContainer extends React.Component { + constructor(props) { + super(props); + this.CommandCell = MyCommandCell(this.remove); + } + + addItem = () => { + this.props.addProduct(); + } + + handleItemChange = (dataItem) => { + this.props.updateProduct(dataItem) + } + + handleStateChange = (event) => { + this.props.datastateChange(event) + } + + remove = (dataItem) => { + this.props.deleteProduct(dataItem); + } + + rowClick = (event) => { + this.props.changeEdit(event.dataItem); + } + + render() { + const processedProducts = process(this.props.products, this.props.dataState) + console.log(this.props.products.length) + return ( +
+ + + + + + + + + + + + +
+ ); + } +} + +const mapDispatchToProps = dispatch => { + return { + onUndo: () => dispatch(UndoActionCreators.undo()), + onRedo: () => dispatch(UndoActionCreators.redo()), + addProduct: () => dispatch(addProduct()), + updateProduct: (dataItem) => dispatch(updateProduct(dataItem)), + deleteProduct: (dataItem) => dispatch(deleteProduct(dataItem)), + changeEdit: (row) => dispatch(changeEdit(row)), + datastateChange: (event) => dispatch(datastateChange(event)) + } + } + +function mapStateToProps(state) { + return { + products: state.products.present.products, + dataState: state.dataState.present, + canUndo: state.products.past.length > 0, + canRedo: state.products.future.length > 0, + dataState: state.dataState.present + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(GridContainer); diff --git a/examples/kendo-react-redux-undo/src/components/MyCommandCell.js b/examples/kendo-react-redux-undo/src/components/MyCommandCell.js new file mode 100644 index 00000000..69329c91 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/components/MyCommandCell.js @@ -0,0 +1,17 @@ +import React from 'react'; + +export default function MyCommandCell(remove) { + return class extends React.Component { + render() { + return ( + + + + ); + } + } +}; \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/src/data/products.js b/examples/kendo-react-redux-undo/src/data/products.js new file mode 100644 index 00000000..f603e1b4 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/data/products.js @@ -0,0 +1,1233 @@ +export const sampleProducts = [{ + "ProductID" : 1, + "ProductName" : "Chai", + "SupplierID" : 1, + "CategoryID" : 1, + "QuantityPerUnit" : "10 boxes x 20 bags", + "UnitPrice" : 18.0000, + "UnitsInStock" : 39, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 2, + "ProductName" : "Chang", + "SupplierID" : 1, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 19.0000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 40, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 3, + "ProductName" : "Aniseed Syrup", + "SupplierID" : 1, + "CategoryID" : 2, + "QuantityPerUnit" : "12 - 550 ml bottles", + "UnitPrice" : 10.0000, + "UnitsInStock" : 13, + "UnitsOnOrder" : 70, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 4, + "ProductName" : "Chef Anton's Cajun Seasoning", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "48 - 6 oz jars", + "UnitPrice" : 22.0000, + "UnitsInStock" : 53, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 5, + "ProductName" : "Chef Anton's Gumbo Mix", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "36 boxes", + "UnitPrice" : 21.3500, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 6, + "ProductName" : "Grandma's Boysenberry Spread", + "SupplierID" : 3, + "CategoryID" : 2, + "QuantityPerUnit" : "12 - 8 oz jars", + "UnitPrice" : 25.0000, + "UnitsInStock" : 120, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 7, + "ProductName" : "Uncle Bob's Organic Dried Pears", + "SupplierID" : 3, + "CategoryID" : 7, + "QuantityPerUnit" : "12 - 1 lb pkgs.", + "UnitPrice" : 30.0000, + "UnitsInStock" : 15, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 8, + "ProductName" : "Northwoods Cranberry Sauce", + "SupplierID" : 3, + "CategoryID" : 2, + "QuantityPerUnit" : "12 - 12 oz jars", + "UnitPrice" : 40.0000, + "UnitsInStock" : 6, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 9, + "ProductName" : "Mishi Kobe Niku", + "SupplierID" : 4, + "CategoryID" : 6, + "QuantityPerUnit" : "18 - 500 g pkgs.", + "UnitPrice" : 97.0000, + "UnitsInStock" : 29, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 10, + "ProductName" : "Ikura", + "SupplierID" : 4, + "CategoryID" : 8, + "QuantityPerUnit" : "12 - 200 ml jars", + "UnitPrice" : 31.0000, + "UnitsInStock" : 31, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 11, + "ProductName" : "Queso Cabrales", + "SupplierID" : 5, + "CategoryID" : 4, + "QuantityPerUnit" : "1 kg pkg.", + "UnitPrice" : 21.0000, + "UnitsInStock" : 22, + "UnitsOnOrder" : 30, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 12, + "ProductName" : "Queso Manchego La Pastora", + "SupplierID" : 5, + "CategoryID" : 4, + "QuantityPerUnit" : "10 - 500 g pkgs.", + "UnitPrice" : 38.0000, + "UnitsInStock" : 86, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 13, + "ProductName" : "Konbu", + "SupplierID" : 6, + "CategoryID" : 8, + "QuantityPerUnit" : "2 kg box", + "UnitPrice" : 6.0000, + "UnitsInStock" : 24, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 14, + "ProductName" : "Tofu", + "SupplierID" : 6, + "CategoryID" : 7, + "QuantityPerUnit" : "40 - 100 g pkgs.", + "UnitPrice" : 23.2500, + "UnitsInStock" : 35, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 15, + "ProductName" : "Genen Shouyu", + "SupplierID" : 6, + "CategoryID" : 2, + "QuantityPerUnit" : "24 - 250 ml bottles", + "UnitPrice" : 15.5000, + "UnitsInStock" : 39, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 16, + "ProductName" : "Pavlova", + "SupplierID" : 7, + "CategoryID" : 3, + "QuantityPerUnit" : "32 - 500 g boxes", + "UnitPrice" : 17.4500, + "UnitsInStock" : 29, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 17, + "ProductName" : "Alice Mutton", + "SupplierID" : 7, + "CategoryID" : 6, + "QuantityPerUnit" : "20 - 1 kg tins", + "UnitPrice" : 39.0000, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 18, + "ProductName" : "Carnarvon Tigers", + "SupplierID" : 7, + "CategoryID" : 8, + "QuantityPerUnit" : "16 kg pkg.", + "UnitPrice" : 62.5000, + "UnitsInStock" : 42, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 19, + "ProductName" : "Teatime Chocolate Biscuits", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "10 boxes x 12 pieces", + "UnitPrice" : 9.2000, + "UnitsInStock" : 25, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 20, + "ProductName" : "Sir Rodney's Marmalade", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "30 gift boxes", + "UnitPrice" : 81.0000, + "UnitsInStock" : 40, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 21, + "ProductName" : "Sir Rodney's Scones", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "24 pkgs. x 4 pieces", + "UnitPrice" : 10.0000, + "UnitsInStock" : 3, + "UnitsOnOrder" : 40, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 22, + "ProductName" : "Gustaf's Knäckebröd", + "SupplierID" : 9, + "CategoryID" : 5, + "QuantityPerUnit" : "24 - 500 g pkgs.", + "UnitPrice" : 21.0000, + "UnitsInStock" : 104, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 23, + "ProductName" : "Tunnbröd", + "SupplierID" : 9, + "CategoryID" : 5, + "QuantityPerUnit" : "12 - 250 g pkgs.", + "UnitPrice" : 9.0000, + "UnitsInStock" : 61, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 24, + "ProductName" : "Guaraná Fantástica", + "SupplierID" : 10, + "CategoryID" : 1, + "QuantityPerUnit" : "12 - 355 ml cans", + "UnitPrice" : 4.5000, + "UnitsInStock" : 20, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 25, + "ProductName" : "NuNuCa Nuß-Nougat-Creme", + "SupplierID" : 11, + "CategoryID" : 3, + "QuantityPerUnit" : "20 - 450 g glasses", + "UnitPrice" : 14.0000, + "UnitsInStock" : 76, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 26, + "ProductName" : "Gumbär Gummibärchen", + "SupplierID" : 11, + "CategoryID" : 3, + "QuantityPerUnit" : "100 - 250 g bags", + "UnitPrice" : 31.2300, + "UnitsInStock" : 15, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 27, + "ProductName" : "Schoggi Schokolade", + "SupplierID" : 11, + "CategoryID" : 3, + "QuantityPerUnit" : "100 - 100 g pieces", + "UnitPrice" : 43.9000, + "UnitsInStock" : 49, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 28, + "ProductName" : "Rössle Sauerkraut", + "SupplierID" : 12, + "CategoryID" : 7, + "QuantityPerUnit" : "25 - 825 g cans", + "UnitPrice" : 45.6000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 29, + "ProductName" : "Thüringer Rostbratwurst", + "SupplierID" : 12, + "CategoryID" : 6, + "QuantityPerUnit" : "50 bags x 30 sausgs.", + "UnitPrice" : 123.7900, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 30, + "ProductName" : "Nord-Ost Matjeshering", + "SupplierID" : 13, + "CategoryID" : 8, + "QuantityPerUnit" : "10 - 200 g glasses", + "UnitPrice" : 25.8900, + "UnitsInStock" : 10, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 31, + "ProductName" : "Gorgonzola Telino", + "SupplierID" : 14, + "CategoryID" : 4, + "QuantityPerUnit" : "12 - 100 g pkgs", + "UnitPrice" : 12.5000, + "UnitsInStock" : 0, + "UnitsOnOrder" : 70, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 32, + "ProductName" : "Mascarpone Fabioli", + "SupplierID" : 14, + "CategoryID" : 4, + "QuantityPerUnit" : "24 - 200 g pkgs.", + "UnitPrice" : 32.0000, + "UnitsInStock" : 9, + "UnitsOnOrder" : 40, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 33, + "ProductName" : "Geitost", + "SupplierID" : 15, + "CategoryID" : 4, + "QuantityPerUnit" : "500 g", + "UnitPrice" : 2.5000, + "UnitsInStock" : 112, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 34, + "ProductName" : "Sasquatch Ale", + "SupplierID" : 16, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 14.0000, + "UnitsInStock" : 111, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 35, + "ProductName" : "Steeleye Stout", + "SupplierID" : 16, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 18.0000, + "UnitsInStock" : 20, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 36, + "ProductName" : "Inlagd Sill", + "SupplierID" : 17, + "CategoryID" : 8, + "QuantityPerUnit" : "24 - 250 g jars", + "UnitPrice" : 19.0000, + "UnitsInStock" : 112, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 37, + "ProductName" : "Gravad lax", + "SupplierID" : 17, + "CategoryID" : 8, + "QuantityPerUnit" : "12 - 500 g pkgs.", + "UnitPrice" : 26.0000, + "UnitsInStock" : 11, + "UnitsOnOrder" : 50, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 38, + "ProductName" : "Côte de Blaye", + "SupplierID" : 18, + "CategoryID" : 1, + "QuantityPerUnit" : "12 - 75 cl bottles", + "UnitPrice" : 263.5000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 39, + "ProductName" : "Chartreuse verte", + "SupplierID" : 18, + "CategoryID" : 1, + "QuantityPerUnit" : "750 cc per bottle", + "UnitPrice" : 18.0000, + "UnitsInStock" : 69, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 40, + "ProductName" : "Boston Crab Meat", + "SupplierID" : 19, + "CategoryID" : 8, + "QuantityPerUnit" : "24 - 4 oz tins", + "UnitPrice" : 18.4000, + "UnitsInStock" : 123, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 41, + "ProductName" : "Jack's New England Clam Chowder", + "SupplierID" : 19, + "CategoryID" : 8, + "QuantityPerUnit" : "12 - 12 oz cans", + "UnitPrice" : 9.6500, + "UnitsInStock" : 85, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 42, + "ProductName" : "Singaporean Hokkien Fried Mee", + "SupplierID" : 20, + "CategoryID" : 5, + "QuantityPerUnit" : "32 - 1 kg pkgs.", + "UnitPrice" : 14.0000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 43, + "ProductName" : "Ipoh Coffee", + "SupplierID" : 20, + "CategoryID" : 1, + "QuantityPerUnit" : "16 - 500 g tins", + "UnitPrice" : 46.0000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 10, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 44, + "ProductName" : "Gula Malacca", + "SupplierID" : 20, + "CategoryID" : 2, + "QuantityPerUnit" : "20 - 2 kg bags", + "UnitPrice" : 19.4500, + "UnitsInStock" : 27, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 45, + "ProductName" : "Rogede sild", + "SupplierID" : 21, + "CategoryID" : 8, + "QuantityPerUnit" : "1k pkg.", + "UnitPrice" : 9.5000, + "UnitsInStock" : 5, + "UnitsOnOrder" : 70, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 46, + "ProductName" : "Spegesild", + "SupplierID" : 21, + "CategoryID" : 8, + "QuantityPerUnit" : "4 - 450 g glasses", + "UnitPrice" : 12.0000, + "UnitsInStock" : 95, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 47, + "ProductName" : "Zaanse koeken", + "SupplierID" : 22, + "CategoryID" : 3, + "QuantityPerUnit" : "10 - 4 oz boxes", + "UnitPrice" : 9.5000, + "UnitsInStock" : 36, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 48, + "ProductName" : "Chocolade", + "SupplierID" : 22, + "CategoryID" : 3, + "QuantityPerUnit" : "10 pkgs.", + "UnitPrice" : 12.7500, + "UnitsInStock" : 15, + "UnitsOnOrder" : 70, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 49, + "ProductName" : "Maxilaku", + "SupplierID" : 23, + "CategoryID" : 3, + "QuantityPerUnit" : "24 - 50 g pkgs.", + "UnitPrice" : 20.0000, + "UnitsInStock" : 10, + "UnitsOnOrder" : 60, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 50, + "ProductName" : "Valkoinen suklaa", + "SupplierID" : 23, + "CategoryID" : 3, + "QuantityPerUnit" : "12 - 100 g bars", + "UnitPrice" : 16.2500, + "UnitsInStock" : 65, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 51, + "ProductName" : "Manjimup Dried Apples", + "SupplierID" : 24, + "CategoryID" : 7, + "QuantityPerUnit" : "50 - 300 g pkgs.", + "UnitPrice" : 53.0000, + "UnitsInStock" : 20, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 52, + "ProductName" : "Filo Mix", + "SupplierID" : 24, + "CategoryID" : 5, + "QuantityPerUnit" : "16 - 2 kg boxes", + "UnitPrice" : 7.0000, + "UnitsInStock" : 38, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 53, + "ProductName" : "Perth Pasties", + "SupplierID" : 24, + "CategoryID" : 6, + "QuantityPerUnit" : "48 pieces", + "UnitPrice" : 32.8000, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 54, + "ProductName" : "Tourtière", + "SupplierID" : 25, + "CategoryID" : 6, + "QuantityPerUnit" : "16 pies", + "UnitPrice" : 7.4500, + "UnitsInStock" : 21, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 55, + "ProductName" : "Pâté chinois", + "SupplierID" : 25, + "CategoryID" : 6, + "QuantityPerUnit" : "24 boxes x 2 pies", + "UnitPrice" : 24.0000, + "UnitsInStock" : 115, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 56, + "ProductName" : "Gnocchi di nonna Alice", + "SupplierID" : 26, + "CategoryID" : 5, + "QuantityPerUnit" : "24 - 250 g pkgs.", + "UnitPrice" : 38.0000, + "UnitsInStock" : 21, + "UnitsOnOrder" : 10, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 57, + "ProductName" : "Ravioli Angelo", + "SupplierID" : 26, + "CategoryID" : 5, + "QuantityPerUnit" : "24 - 250 g pkgs.", + "UnitPrice" : 19.5000, + "UnitsInStock" : 36, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 58, + "ProductName" : "Escargots de Bourgogne", + "SupplierID" : 27, + "CategoryID" : 8, + "QuantityPerUnit" : "24 pieces", + "UnitPrice" : 13.2500, + "UnitsInStock" : 62, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 59, + "ProductName" : "Raclette Courdavault", + "SupplierID" : 28, + "CategoryID" : 4, + "QuantityPerUnit" : "5 kg pkg.", + "UnitPrice" : 55.0000, + "UnitsInStock" : 79, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 60, + "ProductName" : "Camembert Pierrot", + "SupplierID" : 28, + "CategoryID" : 4, + "QuantityPerUnit" : "15 - 300 g rounds", + "UnitPrice" : 34.0000, + "UnitsInStock" : 19, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 61, + "ProductName" : "Sirop d'érable", + "SupplierID" : 29, + "CategoryID" : 2, + "QuantityPerUnit" : "24 - 500 ml bottles", + "UnitPrice" : 28.5000, + "UnitsInStock" : 113, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 62, + "ProductName" : "Tarte au sucre", + "SupplierID" : 29, + "CategoryID" : 3, + "QuantityPerUnit" : "48 pies", + "UnitPrice" : 49.3000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 63, + "ProductName" : "Vegie-spread", + "SupplierID" : 7, + "CategoryID" : 2, + "QuantityPerUnit" : "15 - 625 g jars", + "UnitPrice" : 43.9000, + "UnitsInStock" : 24, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 64, + "ProductName" : "Wimmers gute Semmelknödel", + "SupplierID" : 12, + "CategoryID" : 5, + "QuantityPerUnit" : "20 bags x 4 pieces", + "UnitPrice" : 33.2500, + "UnitsInStock" : 22, + "UnitsOnOrder" : 80, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 65, + "ProductName" : "Louisiana Fiery Hot Pepper Sauce", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "32 - 8 oz bottles", + "UnitPrice" : 21.0500, + "UnitsInStock" : 76, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 66, + "ProductName" : "Louisiana Hot Spiced Okra", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "24 - 8 oz jars", + "UnitPrice" : 17.0000, + "UnitsInStock" : 4, + "UnitsOnOrder" : 100, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 67, + "ProductName" : "Laughing Lumberjack Lager", + "SupplierID" : 16, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 14.0000, + "UnitsInStock" : 52, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 68, + "ProductName" : "Scottish Longbreads", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "10 boxes x 8 pieces", + "UnitPrice" : 12.5000, + "UnitsInStock" : 6, + "UnitsOnOrder" : 10, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 69, + "ProductName" : "Gudbrandsdalsost", + "SupplierID" : 15, + "CategoryID" : 4, + "QuantityPerUnit" : "10 kg pkg.", + "UnitPrice" : 36.0000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 70, + "ProductName" : "Outback Lager", + "SupplierID" : 7, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 355 ml bottles", + "UnitPrice" : 15.0000, + "UnitsInStock" : 15, + "UnitsOnOrder" : 10, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 71, + "ProductName" : "Flotemysost", + "SupplierID" : 15, + "CategoryID" : 4, + "QuantityPerUnit" : "10 - 500 g pkgs.", + "UnitPrice" : 21.5000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 72, + "ProductName" : "Mozzarella di Giovanni", + "SupplierID" : 14, + "CategoryID" : 4, + "QuantityPerUnit" : "24 - 200 g pkgs.", + "UnitPrice" : 34.8000, + "UnitsInStock" : 14, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 73, + "ProductName" : "Röd Kaviar", + "SupplierID" : 17, + "CategoryID" : 8, + "QuantityPerUnit" : "24 - 150 g jars", + "UnitPrice" : 15.0000, + "UnitsInStock" : 101, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 74, + "ProductName" : "Longlife Tofu", + "SupplierID" : 4, + "CategoryID" : 7, + "QuantityPerUnit" : "5 kg pkg.", + "UnitPrice" : 10.0000, + "UnitsInStock" : 4, + "UnitsOnOrder" : 20, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 75, + "ProductName" : "Rhönbräu Klosterbier", + "SupplierID" : 12, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 0.5 l bottles", + "UnitPrice" : 7.7500, + "UnitsInStock" : 125, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 76, + "ProductName" : "Lakkalikööri", + "SupplierID" : 23, + "CategoryID" : 1, + "QuantityPerUnit" : "500 ml", + "UnitPrice" : 18.0000, + "UnitsInStock" : 57, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 77, + "ProductName" : "Original Frankfurter grüne Soße", + "SupplierID" : 12, + "CategoryID" : 2, + "QuantityPerUnit" : "12 boxes", + "UnitPrice" : 13.0000, + "UnitsInStock" : 32, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}]; diff --git a/examples/kendo-react-redux-undo/src/index.css b/examples/kendo-react-redux-undo/src/index.css new file mode 100644 index 00000000..6c39d0eb --- /dev/null +++ b/examples/kendo-react-redux-undo/src/index.css @@ -0,0 +1,9 @@ +body { + margin: 0; + padding: 0; + font-family: sans-serif; +} + +.button-right { + float: left; +} diff --git a/examples/kendo-react-redux-undo/src/index.js b/examples/kendo-react-redux-undo/src/index.js new file mode 100644 index 00000000..4223cac0 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/index.js @@ -0,0 +1,15 @@ +import React from 'react'; +import './index.css'; +import App from './App'; +import { render } from 'react-dom' +import { Provider } from 'react-redux' +import { store } from './reducers/index' +  + +  +render( + + + , + document.getElementById('root') +) \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/src/logo.svg b/examples/kendo-react-redux-undo/src/logo.svg new file mode 100644 index 00000000..6b60c104 --- /dev/null +++ b/examples/kendo-react-redux-undo/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/kendo-react-redux-undo/src/reducers/index.js b/examples/kendo-react-redux-undo/src/reducers/index.js new file mode 100644 index 00000000..dabd1bba --- /dev/null +++ b/examples/kendo-react-redux-undo/src/reducers/index.js @@ -0,0 +1,7 @@ +import { createStore, applyMiddleware, combineReducers } from 'redux'; +import { undoableProducts, undoableDataState } from './reducers'; +import { composeWithDevTools } from 'redux-devtools-extension' +import thunk from 'redux-thunk' + +const rootReducer = combineReducers({products: undoableProducts, dataState: undoableDataState}) +export const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk))) \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/src/reducers/reducers.js b/examples/kendo-react-redux-undo/src/reducers/reducers.js new file mode 100644 index 00000000..2070de9e --- /dev/null +++ b/examples/kendo-react-redux-undo/src/reducers/reducers.js @@ -0,0 +1,70 @@ +import { + ADD_PRODUCT, + UPDATE_PRODUCT, + DELETE_PRODUCT, + CHANGE_EDIT, + DATASTATE_CHANGE + } from '../actions/actions' +import { sampleProducts } from '../data/products' +import undoable from 'redux-undo' + +export function productsReducer(state = {products: sampleProducts}, action) { + switch (action.type) { + + case ADD_PRODUCT: + let makeNewID = Math.random() * new Date().getMilliseconds() * 100 + let newProductID = Math.floor(makeNewID) + let productForAdd = {inEdit: true} + productForAdd.ProductID = newProductID; + let productsAfterAdd = state.products.slice() + productsAfterAdd.map(product => product.inEdit = false) + productsAfterAdd.unshift(productForAdd) + return Object.assign({ products: productsAfterAdd }, {}); + + case UPDATE_PRODUCT: + const newProducts = state.products.map(product => { + const newProduct = { + ...product + }; + if(product.ProductID === action.payload.dataItem.ProductID) { + newProduct[action.payload.field] = action.payload.value; + } + return newProduct; + }) + return Object.assign({ products: newProducts }, {}); + + case DELETE_PRODUCT: + let index = state.products.findIndex(product => product.ProductID === action.payload.ProductID); + let productsAfterDelete = state.products.slice() + productsAfterDelete.splice(index, 1) + return Object.assign({ products: productsAfterDelete }, {}); + case CHANGE_EDIT: + const newProductsSelection = state.products.map(product => { + const newProduct = { + ...product + }; + if(product.ProductID === action.payload.ProductID) { + newProduct.inEdit = true + } else { + newProduct.inEdit = false + } + return newProduct; + }) + return Object.assign({ products: newProductsSelection }, {}); + default: + return state + } +} + +export function gridStateReducer(state = {sort: [], filter:[], skip: 0, take: 10}, action) { + switch (action.type) { + case DATASTATE_CHANGE: + const dataState = action.payload.data + return Object.assign({ sort: dataState.sort, filter: dataState.filter, skip: dataState.skip, take: dataState.take }, {}); + default: + return state + } +} + +export const undoableProducts = undoable(productsReducer) +export const undoableDataState = undoable(gridStateReducer) \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/src/registerServiceWorker.js b/examples/kendo-react-redux-undo/src/registerServiceWorker.js new file mode 100644 index 00000000..a3e6c0cf --- /dev/null +++ b/examples/kendo-react-redux-undo/src/registerServiceWorker.js @@ -0,0 +1,117 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export default function register() { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://goo.gl/SC7cgQ' + ); + }); + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } + }); + } +} + +function registerValidSW(swUrl) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} From b27416b997c11437768e0b8f1fe1a6f03cc7c210 Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Sun, 1 Jun 2025 22:19:41 +0300 Subject: [PATCH 2/2] chore: bump versions for redux-undo app --- examples/kendo-react-redux-undo/package.json | 38 ++++++++++--------- .../kendo-react-redux-undo/src/App.test.js | 7 ++-- .../src/components/GridContainer.js | 34 +++++++++-------- examples/kendo-react-redux-undo/src/index.js | 18 ++++----- .../src/reducers/index.js | 15 +++++--- .../src/reducers/reducers.js | 14 ++++--- 6 files changed, 71 insertions(+), 55 deletions(-) diff --git a/examples/kendo-react-redux-undo/package.json b/examples/kendo-react-redux-undo/package.json index d6eb8426..5f07671c 100644 --- a/examples/kendo-react-redux-undo/package.json +++ b/examples/kendo-react-redux-undo/package.json @@ -3,24 +3,26 @@ "version": "0.1.0", "private": true, "dependencies": { - "@progress/kendo-data-query": "^1.5.0", - "@progress/kendo-drawing": "^1.5.7", - "@progress/kendo-react-dateinputs": "^1.2.0", - "@progress/kendo-react-dropdowns": "^1.2.0", - "@progress/kendo-react-grid": "^1.2.0", - "@progress/kendo-react-inputs": "^1.2.0", - "@progress/kendo-react-intl": "^1.2.0", - "@progress/kendo-react-pdf": "^1.2.0", - "@progress/kendo-theme-default": "^2.55.0", - "react": "^16.4.2", - "react-dom": "^16.4.2", - "react-redux": "^5.0.7", - "react-scripts": "1.1.5", - "redux": "^4.0.0", - "redux-devtools-extension": "^2.13.5", - "redux-thunk": "^2.3.0", - "redux-undo": "^0.6.1", - "prop-types": "15.6.2" + "@progress/kendo-data-query": "^1.7.1", + "@progress/kendo-drawing": "^1.21.2", + "@progress/kendo-react-dateinputs": "^11.0.0", + "@progress/kendo-react-dropdowns": "^11.0.0", + "@progress/kendo-react-grid": "^11.0.0", + "@progress/kendo-react-inputs": "^11.0.0", + "@progress/kendo-react-intl": "^11.0.0", + "@progress/kendo-react-pdf": "^11.0.0", + "@progress/kendo-react-data-tools": "^11.0.0", + "@progress/kendo-theme-default": "^11.0.2", + "react": "^18", + "react-dom": "^18", + "react-redux": "^9.2.0", + "react-scripts": "5.0.1", + "redux": "^5.0.1", + "@redux-devtools/extension": "^3.3.0", + "@reduxjs/toolkit": "^2.8.2", + "redux-thunk": "^3.1.0", + "redux-undo": "^1.1.0", + "prop-types": "15.8.1" }, "scripts": { "start": "react-scripts start", diff --git a/examples/kendo-react-redux-undo/src/App.test.js b/examples/kendo-react-redux-undo/src/App.test.js index a754b201..fb712258 100644 --- a/examples/kendo-react-redux-undo/src/App.test.js +++ b/examples/kendo-react-redux-undo/src/App.test.js @@ -1,9 +1,10 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import ReactDOM from 'react-dom/client'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); + const root = ReactDOM.createRoot(div); + root.render(); + root.unmount(); }); diff --git a/examples/kendo-react-redux-undo/src/components/GridContainer.js b/examples/kendo-react-redux-undo/src/components/GridContainer.js index a9170522..0d736095 100644 --- a/examples/kendo-react-redux-undo/src/components/GridContainer.js +++ b/examples/kendo-react-redux-undo/src/components/GridContainer.js @@ -1,10 +1,12 @@ import React from 'react'; -import {connect} from 'react-redux'; -import {addProduct, updateProduct, deleteProduct, changeEdit, datastateChange} from '../actions/actions'; -import MyCommandCell from './MyCommandCell' +import { connect } from 'react-redux'; import { ActionCreators as UndoActionCreators } from 'redux-undo'; -import {Grid, GridColumn as Column, GridToolbar} from '@progress/kendo-react-grid'; +import { addProduct, updateProduct, deleteProduct, changeEdit, datastateChange } from '../actions/actions'; +import { Grid, GridColumn as Column, GridToolbar, GridToolbarSort, GridToolbarFilter } from '@progress/kendo-react-grid'; +import { Button } from '@progress/kendo-react-buttons'; +import { gearIcon } from '@progress/kendo-svg-icons'; import { process } from '@progress/kendo-data-query' +import MyCommandCell from './MyCommandCell' class GridContainer extends React.Component { constructor(props) { @@ -38,37 +40,39 @@ class GridContainer extends React.Component { return (
- - - + diff --git a/examples/kendo-react-redux-undo/src/index.js b/examples/kendo-react-redux-undo/src/index.js index 4223cac0..ff78f977 100644 --- a/examples/kendo-react-redux-undo/src/index.js +++ b/examples/kendo-react-redux-undo/src/index.js @@ -1,15 +1,15 @@ import React from 'react'; import './index.css'; import App from './App'; -import { render } from 'react-dom' -import { Provider } from 'react-redux' -import { store } from './reducers/index' -  +import { Provider } from 'react-redux'; +import { store } from './reducers/index'; +import { createRoot } from 'react-dom/client'; -  -render( +const container = document.getElementById('root'); +const root = createRoot(container); + +root.render( - , - document.getElementById('root') -) \ No newline at end of file + +); \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/src/reducers/index.js b/examples/kendo-react-redux-undo/src/reducers/index.js index dabd1bba..335296fd 100644 --- a/examples/kendo-react-redux-undo/src/reducers/index.js +++ b/examples/kendo-react-redux-undo/src/reducers/index.js @@ -1,7 +1,12 @@ -import { createStore, applyMiddleware, combineReducers } from 'redux'; +import { configureStore } from '@reduxjs/toolkit'; +import { combineReducers } from 'redux'; import { undoableProducts, undoableDataState } from './reducers'; -import { composeWithDevTools } from 'redux-devtools-extension' -import thunk from 'redux-thunk' -const rootReducer = combineReducers({products: undoableProducts, dataState: undoableDataState}) -export const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk))) \ No newline at end of file +const rootReducer = combineReducers({ + products: undoableProducts, + dataState: undoableDataState, +}); + +export const store = configureStore({ + reducer: rootReducer +}); \ No newline at end of file diff --git a/examples/kendo-react-redux-undo/src/reducers/reducers.js b/examples/kendo-react-redux-undo/src/reducers/reducers.js index 2070de9e..0f6b3668 100644 --- a/examples/kendo-react-redux-undo/src/reducers/reducers.js +++ b/examples/kendo-react-redux-undo/src/reducers/reducers.js @@ -14,12 +14,16 @@ export function productsReducer(state = {products: sampleProducts}, action) { case ADD_PRODUCT: let makeNewID = Math.random() * new Date().getMilliseconds() * 100 let newProductID = Math.floor(makeNewID) - let productForAdd = {inEdit: true} - productForAdd.ProductID = newProductID; - let productsAfterAdd = state.products.slice() - productsAfterAdd.map(product => product.inEdit = false) + let productForAdd = { inEdit: true, ProductID: newProductID }; + let productsAfterAdd = state.products.map(product => ({ + ...product, + inEdit: false + })); productsAfterAdd.unshift(productForAdd) - return Object.assign({ products: productsAfterAdd }, {}); + return { + ...state, + products: productsAfterAdd + }; case UPDATE_PRODUCT: const newProducts = state.products.map(product => {