diff --git a/App.tsx b/App.tsx new file mode 100644 index 000000000..192829805 --- /dev/null +++ b/App.tsx @@ -0,0 +1,132 @@ +import { hot } from 'react-hot-loader/root' +import React, { useCallback, useEffect, useState } from 'react' +import { Switch, useHistory, useLocation, Redirect } from 'react-router-dom' +import { useRecoilState } from 'recoil' +import { Swipeable } from 'react-swipeable' +import PrivateRoutesController from './routes/PrivateRoutesController' +import Navbar from './components/navbar/Nav' +import Sidebar from './components/sidebar/Sidebar' +import PublicRoute from './components/route/PublicRoute' +import AuthPage from './pages/auth/Autho' +import BasicLoader from './components/load/BasicLoader' +import { userState } from './global-state/miscState' +import client from './api/client' + +// Hooks +import useLoadActiveListData from './hooks/useLoadActiveListData' +import useFetchCategories from './hooks/useFetchCategories' +import useFetchItems from './hooks/useFetchItems' +import useLoadHistoryLists from './hooks/useFetchHistoryLists' +import useSidebarShow from './hooks/useSidebarShow' +const App: React.FC = () => { + const [user, setUser] = useRecoilState(userState) + const [init, setInit] = useState(true) + const [lastHeight, setLastHeight] = useState(0) + const history = useHistory() + const location = useLocation() + const initialActiveShopListData = useLoadActiveListData() + const fetchCategories = useFetchCategories() + const fetchItems = useFetchItems() + const fetchShopListHistory = useLoadHistoryLists() + const showSidebar = useSidebarShow() + useEffect(() => { + if (location.search.length > 0) { + const access_token = new URLSearchParams(location.search).get( + 'access_token' + ) + if (access_token) { + + localStorage.setItem('token', access_token) + getUserConnected() + } + } + }, [location.search]) + + useEffect(() => { + if (localStorage.getItem('token')) { + // Fetch the user + if (location.search === '') { + getUserConnected() + } + } else { + setInit(false) + history.push('/login') + } + }, []) + + useEffect(() => { + const resize = () => { + let vh = window.innerHeight * 0.01 + document.documentElement.style.setProperty('--vh', `${vh}px`) + } + + resize() + window.addEventListener('resize', resize) + return () => { + window.removeEventListener('resize', resize) + } + }, []) + + const initData = async () => { + await fetchCategories() + await fetchItems() + await initialActiveShopListData() + await fetchShopListHistory() + } + + useEffect(() => { + if (user !== null) { + initData() + setInit(false) + } + }, [user]) + + const getUserConnected = useCallback(async () => { + try { + const res = await client.get('me') + const { id } = res.data.data + setUser(id) + } catch (e) { + console.log('Error fetching the connected user', e) + setInit(false) + history.push('/login') + } + }, []) + + if (init) + return ( +
+ +
+ ) + + if (user) { + return ( + { + if (eventData.dir === 'Left' || eventData.dir === 'Right') { + showSidebar(eventData.dir) + } + }} + > +
+ +
+ +
+ +
+
+ ) + } + + return ( + + + + + + ) +} + +export default hot(App) diff --git a/_redirects b/_redirects new file mode 100644 index 000000000..78f7f2067 --- /dev/null +++ b/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/actions.spec.js b/actions.spec.js new file mode 100644 index 000000000..43744cdec --- /dev/null +++ b/actions.spec.js @@ -0,0 +1,299 @@ +/// + +context('Actions', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/actions') + }) + + // https://on.cypress.io/interacting-with-elements + + it('.type() - type into a DOM element', () => { + // https://on.cypress.io/type + cy.get('.action-email') + .type('fake@email.com').should('have.value', 'fake@email.com') + + // .type() with special character sequences + .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') + .type('{del}{selectall}{backspace}') + + // .type() with key modifiers + .type('{alt}{option}') //these are equivalent + .type('{ctrl}{control}') //these are equivalent + .type('{meta}{command}{cmd}') //these are equivalent + .type('{shift}') + + // Delay each keypress by 0.1 sec + .type('slow.typing@email.com', { delay: 100 }) + .should('have.value', 'slow.typing@email.com') + + cy.get('.action-disabled') + // Ignore error checking prior to type + // like whether the input is visible or disabled + .type('disabled error checking', { force: true }) + .should('have.value', 'disabled error checking') + }) + + it('.focus() - focus on a DOM element', () => { + // https://on.cypress.io/focus + cy.get('.action-focus').focus() + .should('have.class', 'focus') + .prev().should('have.attr', 'style', 'color: orange;') + }) + + it('.blur() - blur off a DOM element', () => { + // https://on.cypress.io/blur + cy.get('.action-blur').type('About to blur').blur() + .should('have.class', 'error') + .prev().should('have.attr', 'style', 'color: red;') + }) + + it('.clear() - clears an input or textarea element', () => { + // https://on.cypress.io/clear + cy.get('.action-clear').type('Clear this text') + .should('have.value', 'Clear this text') + .clear() + .should('have.value', '') + }) + + it('.submit() - submit a form', () => { + // https://on.cypress.io/submit + cy.get('.action-form') + .find('[type="text"]').type('HALFOFF') + + cy.get('.action-form').submit() + .next().should('contain', 'Your form has been submitted!') + }) + + it('.click() - click on a DOM element', () => { + // https://on.cypress.io/click + cy.get('.action-btn').click() + + // You can click on 9 specific positions of an element: + // ----------------------------------- + // | topLeft top topRight | + // | | + // | | + // | | + // | left center right | + // | | + // | | + // | | + // | bottomLeft bottom bottomRight | + // ----------------------------------- + + // clicking in the center of the element is the default + cy.get('#action-canvas').click() + + cy.get('#action-canvas').click('topLeft') + cy.get('#action-canvas').click('top') + cy.get('#action-canvas').click('topRight') + cy.get('#action-canvas').click('left') + cy.get('#action-canvas').click('right') + cy.get('#action-canvas').click('bottomLeft') + cy.get('#action-canvas').click('bottom') + cy.get('#action-canvas').click('bottomRight') + + // .click() accepts an x and y coordinate + // that controls where the click occurs :) + + cy.get('#action-canvas') + .click(80, 75) // click 80px on x coord and 75px on y coord + .click(170, 75) + .click(80, 165) + .click(100, 185) + .click(125, 190) + .click(150, 185) + .click(170, 165) + + // click multiple elements by passing multiple: true + cy.get('.action-labels>.label').click({ multiple: true }) + + // Ignore error checking prior to clicking + cy.get('.action-opacity>.btn').click({ force: true }) + }) + + it('.dblclick() - double click on a DOM element', () => { + // https://on.cypress.io/dblclick + + // Our app has a listener on 'dblclick' event in our 'scripts.js' + // that hides the div and shows an input on double click + cy.get('.action-div').dblclick().should('not.be.visible') + cy.get('.action-input-hidden').should('be.visible') + }) + + it('.rightclick() - right click on a DOM element', () => { + // https://on.cypress.io/rightclick + + // Our app has a listener on 'contextmenu' event in our 'scripts.js' + // that hides the div and shows an input on right click + cy.get('.rightclick-action-div').rightclick().should('not.be.visible') + cy.get('.rightclick-action-input-hidden').should('be.visible') + }) + + it('.check() - check a checkbox or radio element', () => { + // https://on.cypress.io/check + + // By default, .check() will check all + // matching checkbox or radio elements in succession, one after another + cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') + .check().should('be.checked') + + cy.get('.action-radios [type="radio"]').not('[disabled]') + .check().should('be.checked') + + // .check() accepts a value argument + cy.get('.action-radios [type="radio"]') + .check('radio1').should('be.checked') + + // .check() accepts an array of values + cy.get('.action-multiple-checkboxes [type="checkbox"]') + .check(['checkbox1', 'checkbox2']).should('be.checked') + + // Ignore error checking prior to checking + cy.get('.action-checkboxes [disabled]') + .check({ force: true }).should('be.checked') + + cy.get('.action-radios [type="radio"]') + .check('radio3', { force: true }).should('be.checked') + }) + + it('.uncheck() - uncheck a checkbox element', () => { + // https://on.cypress.io/uncheck + + // By default, .uncheck() will uncheck all matching + // checkbox elements in succession, one after another + cy.get('.action-check [type="checkbox"]') + .not('[disabled]') + .uncheck().should('not.be.checked') + + // .uncheck() accepts a value argument + cy.get('.action-check [type="checkbox"]') + .check('checkbox1') + .uncheck('checkbox1').should('not.be.checked') + + // .uncheck() accepts an array of values + cy.get('.action-check [type="checkbox"]') + .check(['checkbox1', 'checkbox3']) + .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') + + // Ignore error checking prior to unchecking + cy.get('.action-check [disabled]') + .uncheck({ force: true }).should('not.be.checked') + }) + + it('.select() - select an option in a