diff --git a/client/DESIGN.md b/client/DESIGN.md index 7a37ff7..d2c6fe7 100644 --- a/client/DESIGN.md +++ b/client/DESIGN.md @@ -28,6 +28,38 @@ This file contains the rough design documentation for the front end of this site - We can store JWT on the client side in either localStorage or cookies. localStorage is vulnerable to XSS(Cross-Site Scripting) attacks, while cookies are vulnerable to CSRF(Cross Site Request Forgery) attacks. After doing some research on this it seems like storing the JWT in cookies is the better option as CSRF is easier to mitigate than XSS. ## Flows +### Get single folder +#### Router +- User should be able to navigate to single folder page +- react router should support this under `/folders/:id` route + +#### Page component +- `componentDidMount()` should fetch individual folder information. +- storage should be done in REDUX store + - This is so when the user wants to edit the page(if they have permission) the information will already be fetched. +- `fetchSingleFolder(folderId)` action, should get `folderId` from react-router `:id` parameter. + - dispatch fetch `action` + - `saga` should perform api call, and dispatch `folderGetSuccess` or `folderGetFailure` + - `reducer` should handle following entities: + - `folder` data + - `chip_copies` data + - `author` data(may not be necessary, only 1 author) + - `reducer` state should be normalized + + #### Chip Order + - For chip order, there will be an `index` column for each chip copy + - the SERVER will be returning the order of the chip copies, by their id + - the client will store the order as an array of ids + - the client will IGNORE the `index` column for each chip_copy, and instead will order by the returned array of ids. + - on EDIT SUCCESS + - update item in byId list + - update order property: array of ids + - on DELETE SUCCESS + - remote item from byId list + - update order property: array of ids + + + ### Register Account Flow - User Opens Register modal - User enters information @@ -50,4 +82,5 @@ This file contains the rough design documentation for the front end of this site ## Redux, Components, Design Patterns - Problem: child components are nested inside of a container and need actions/state from redux store - This means we would have to pass state/actions through multiple child components who don't use the action/state we're passing through until it reaches the nested child that actually uses it. -- Solution: Organize by feature, if passing child props through too many components then consider adding the props directly to the needed container \ No newline at end of file +- Solution: Organize by feature, if passing child props through too many components then consider adding the props directly to the needed container + diff --git a/client/package.json b/client/package.json index b550c44..521cfac 100644 --- a/client/package.json +++ b/client/package.json @@ -1,5 +1,5 @@ { - "name": "webpack-demo", + "name": "xtra-redux client", "version": "1.0.0", "description": "", "private": true, diff --git a/client/src/App.jsx b/client/src/App.jsx index f208e2e..0e1a127 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -17,6 +17,7 @@ import Login from './views/pages/Login.jsx'; import Forbidden from './views/pages/Forbidden.jsx'; import NotFound from './views/pages/NotFound.jsx'; import SearchFolders from './views/pages/SearchFolders.jsx'; +import Folder from './views/pages/Folder.jsx'; import RequireRole from './views/components/RequireRole/index.jsx'; const App = () => { @@ -31,7 +32,8 @@ const App = () => { - + + diff --git a/client/src/theme.scss b/client/src/theme.scss index 6eccd4d..eb3a657 100644 --- a/client/src/theme.scss +++ b/client/src/theme.scss @@ -4,6 +4,43 @@ // override bulma + add custom scss here +// single folder +.single-folder-container { + @include until($desktop) { + padding: 1rem; + } + + @include from($desktop) { + padding: 1.5rem 0; + } +} + +.chip-body-info { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + + p:not(:last-child){ + padding-right: 1rem; + } +} + +.card-content { + @include until($tablet) { + padding: 1rem; + } +} + +.card:not(:last-child) { + @include until($desktop) { + margin-bottom: 1rem; + } + + @include from($desktop) { + margin-bottom: 1.5rem; + } +} + // sticky footer // TODO: re examine to ensure maximum browser compatibility #app { diff --git a/client/src/views/containers/FoldersList/Folder.jsx b/client/src/views/containers/FoldersList/Folder.jsx index 6878cd3..a2b4a9a 100644 --- a/client/src/views/containers/FoldersList/Folder.jsx +++ b/client/src/views/containers/FoldersList/Folder.jsx @@ -1,11 +1,16 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import PT from 'prop-types'; const Folder = ({ id, title, description, author }) => { return ( {id} - {title} + + + {title} + + {description} {author.username} diff --git a/client/src/views/containers/SingleFolder/ChipList.jsx b/client/src/views/containers/SingleFolder/ChipList.jsx new file mode 100644 index 0000000..47802c3 --- /dev/null +++ b/client/src/views/containers/SingleFolder/ChipList.jsx @@ -0,0 +1,37 @@ +import React, { Fragment } from 'react'; + + +const ChipList = ({chips = null}) => { + return( + + { + chips.map((chip) => { + return ( +
+
+
+

No. {chip.chip_number} - {chip.original_name}

+
+
+
+
+

Element: {chip.element}

+

Memory: {chip.memory}

+

Rarity: {chip.rarity}

+

Type: {chip.type}

+
+
+ +
+ ); + }) + } +
+ ) +} + +export default ChipList; \ No newline at end of file diff --git a/client/src/views/containers/SingleFolder/ChipTable.jsx b/client/src/views/containers/SingleFolder/ChipTable.jsx new file mode 100644 index 0000000..25eb075 --- /dev/null +++ b/client/src/views/containers/SingleFolder/ChipTable.jsx @@ -0,0 +1,39 @@ +import React, { Fragment } from 'react'; +import PT from 'prop-types'; + +const ChipTable = ({chips}) => { + return( +
+ + + + + + + + + + + + + + { + chips.map((chip) => { + return ( + + + + + + + + ) + }) + } + +
No.NameElementDamageCodeMemoryRarity
{chip.chip_number}{chip.original_name}{chip.element}{chip.damage}{chip.code}{chip.memory}{chip.rarity}
+
+ ) +} + +export default ChipTable; \ No newline at end of file diff --git a/client/src/views/containers/SingleFolder/index.jsx b/client/src/views/containers/SingleFolder/index.jsx new file mode 100644 index 0000000..408375f --- /dev/null +++ b/client/src/views/containers/SingleFolder/index.jsx @@ -0,0 +1,34 @@ +import React, { Fragment } from 'react'; +import ChipList from './ChipList.jsx'; +import ChipTable from './ChipTable.jsx' + +class SingleFolder extends React.Component { + constructor(props) { + super(props); + } + + render() { + const { title, author, created_at, child_chips = [] } = this.props; + return ( + +
+
+
+

+ {title} +

+

+ By - {author && author.username} +

+
+
+
+
+ +
+
+ ); + } +} + +export default SingleFolder; \ No newline at end of file diff --git a/client/src/views/pages/Folder.jsx b/client/src/views/pages/Folder.jsx new file mode 100644 index 0000000..9970808 --- /dev/null +++ b/client/src/views/pages/Folder.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import axios from 'axios'; +import SingleFolder from './../containers/SingleFolder/index.jsx'; + +class Folder extends React.Component { + constructor(props) { + super(props); + } + + componentDidMount() { + const BASE_URL = 'http://localhost:3000/api/' + const { match: { params } } = this.props; + axios.get(BASE_URL.concat('folder/', params.id)) + .then((response) => { + this.setState(response.data.data); + console.log(response.data) + }) + } + + render() { + return ( + + ) + } +} + +export default Folder; \ No newline at end of file diff --git a/server/models/folder.js b/server/models/folder.js index d2d415d..aa929cb 100644 --- a/server/models/folder.js +++ b/server/models/folder.js @@ -43,6 +43,7 @@ class Folder extends DbErrors(Sch.Model) { child_chips: { relation: Model.ManyToManyRelation, modelClass: __dirname + '/chip', + filter: query => query.select('chip_copy.code'), join: { from: 'folder.id', // ManyToMany relation needs the `through` object diff --git a/server/routes/v1.js b/server/routes/v1.js index f2445b0..cd7e3f5 100644 --- a/server/routes/v1.js +++ b/server/routes/v1.js @@ -77,10 +77,6 @@ router.post(`${folderBaseUrl}`, // GET#get single folder by id router.get(`${folderBaseUrl}/:id`, - isAllowed.check({ - resource : folderResource, - action: 'read', - }), folderController.get); // PUT#update single folder by id diff --git a/server/test/routes.folder.test.js b/server/test/routes.folder.test.js index 23b2044..a6bff2b 100644 --- a/server/test/routes.folder.test.js +++ b/server/test/routes.folder.test.js @@ -35,7 +35,7 @@ describe('# routes : folder', () => { it('should return all folders', (done) => { testHelper.login(agent, chai).then(() => { agent - .get('/api/folder?sortKey=id&sortDirection=ASC') + .get('/api/folder?sortBy=id&order=ASC') .end((err, res) => { should.not.exist(err); res.status.should.equal(200);