diff --git a/README.md b/README.md index a349ac4b..037f365c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,21 @@ module.exports = { name: "[module-name]", ... +``` +# Run All Modules and Host Application +* Install dependency +``` +yarn install +``` +* Run all modules +``` +yarn start +``` + +# Build Application for Production +``` +yarn build + ``` # Run Module as Standalone Application @@ -38,15 +53,26 @@ lerna run start --scope=[module-name] ``` # Use Module in Host Application -* Add remote module url url to remotes in ```packages/[host-app]/moduleFederation.config.js ``` +* Add remote module url to remotes in ```packages/[host-app]/moduleFederation.config.js ``` ``` # e.g. core module is runninig on localhost:3001 then remotes: { - core: 'core@http://localhost:3001/moduleEntry.js', + core: 'core@[window.appModules.core.url]/remoteEntry.js', }, ``` +* Add entry to ```moodules.json``` +``` +# e.g. core module is runninig on localhost:3001 then + +{ + "core":{ + "url": "http://localhost:3001" + }, + ... +} +``` * To use exposed component from remote module in react. The lazy load componennt must be enclosed within `````` diff --git a/lerna.json b/lerna.json index d6707ca0..352f7710 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,5 @@ { - "packages": [ - "packages/*" - ], - "version": "0.0.0" + "version": "0.0.0", + "npmClient": "yarn", + "useWorkspaces": true } diff --git a/package.json b/package.json index 9a4df050..63a0f41a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,19 @@ { "name": "root", "private": true, + "workspaces": { + "packages": [ + "packages/*" + ] + }, + "scripts": { + "start": "lerna run --parallel start", + "build": "lerna run build", + "serve": "lerna run --parallel serve", + "clean": "lerna run --parallel clean", + "cls-dep": "npx rimraf ./**/node_modules" + }, "devDependencies": { - "lerna": "^4.0.0" + "lerna": "4.0.0" } } diff --git a/packages/attendance/.gitignore b/packages/attendance/.gitignore new file mode 100644 index 00000000..4d29575d --- /dev/null +++ b/packages/attendance/.gitignore @@ -0,0 +1,23 @@ +# 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 + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/packages/attendance/.prettierignore b/packages/attendance/.prettierignore new file mode 100644 index 00000000..216dc2b4 --- /dev/null +++ b/packages/attendance/.prettierignore @@ -0,0 +1,17 @@ +# Dependency directories +node_modules/ + +# dependencies +package.json +package-lock.json + +# testing +/coverage + +# production +/build + +.github/ +/public +.min.js +.min.css diff --git a/packages/attendance/.prettierrc.json b/packages/attendance/.prettierrc.json new file mode 100644 index 00000000..8a645f17 --- /dev/null +++ b/packages/attendance/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "useTabs": false +} diff --git a/packages/attendance/.storybook/main.js b/packages/attendance/.storybook/main.js new file mode 100644 index 00000000..4df7266c --- /dev/null +++ b/packages/attendance/.storybook/main.js @@ -0,0 +1,13 @@ +module.exports = { + stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], + addons: [ + "@storybook/addon-links", + "@storybook/addon-essentials", + "@storybook/addon-interactions", + "@storybook/preset-create-react-app", + ], + framework: "@storybook/react", + core: { + builder: "webpack5", + }, +}; diff --git a/packages/attendance/.storybook/preview.js b/packages/attendance/.storybook/preview.js new file mode 100644 index 00000000..f089c7f9 --- /dev/null +++ b/packages/attendance/.storybook/preview.js @@ -0,0 +1,9 @@ +export const parameters = { + actions: { argTypesRegex: "^on[A-Z].*" }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; diff --git a/packages/attendance/CONTRIBUTING.md b/packages/attendance/CONTRIBUTING.md new file mode 100644 index 00000000..7d751a13 --- /dev/null +++ b/packages/attendance/CONTRIBUTING.md @@ -0,0 +1,202 @@ +# Contributing + +We welcome contributions in several forms, e.g. + +- Improve end user documenting on the [Wiki](https://github.com/Samagra-Development/Worksheet-Module/wiki) + +- Write unit tests and learn how the code works + +- Verify available [patches (pull requests)](https://github.com/Samagra-Development/Worksheet-Module/pulls) + +- Working on [issues](https://github.com/Samagra-Development/Worksheet-Module/issues) + + - Fix a bug + - Add a new feature + +- etc. + +## Reporting Bugs + +Worksheet Module uses GitHub's issue tracker. All bugs and enhancements should be +entered so that we don't lose track of them, can prioritize, assign, and so code +fixes can refer to the bug number in its check-in comments. + +The issue usually contains much more detail (including test cases) than can be +reasonably put in check-in comments, so being able to correlate the two is +important. + +Consider the usual best practice for writing issues, among them: + +- More verbosity rather than one liners +- Screenshots are a great help +- Providing example files (in case for example scanning crashes) +- Please determine the version, better the commit id +- Details on the operating system you are using + +## Git Guidelines + +Not familiar with git, see [Git basic commands](https://git-scm.com/) + +### Workflow + +We are using the [Feature Branch Workflow (also known as GitHub Flow)](https://guides.github.com/introduction/flow/), +and prefer delivery as pull requests. + +Create a feature branch: + +```sh +git checkout -B feat/tune-vagrant-vm +``` + +### Git Commit + +The cardinal rule for creating good commits is to ensure there is only one +"logical change" per commit. Why is this an important rule? + +- The smaller the amount of code being changed, the quicker & easier it is to + review & identify potential flaws. + +- If a change is found to be flawed later, it may be necessary to revert the + broken commit. This is much easier to do if there are no other unrelated + code changes entangled with the original commit. + +- When troubleshooting problems using Git's bisect capability, small well + defined changes will aid in isolating exactly where the code problem was + introduced. + +- When browsing history using Git annotate/blame, small well defined changes + also aid in isolating exactly where & why a piece of code came from. + +Things to avoid when creating commits + +- Mixing whitespace changes with functional code changes. +- Mixing two unrelated functional changes. +- Sending large new features in a single giant commit. + +### Git Commit Conventions + +We use git commit as per [Conventional Changelog](https://www.conventionalcommits.org/en/v1.0.0/): + +```none +(): +``` + +Example: + +```none +feat(vagrant): increase upload size +``` + +Allowed types: + +- **feat**: A new feature +- **fix**: A bug fix +- **docs**: Documentation only changes +- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, newline, line endings, etc) +- **refactor**: A code change that neither fixes a bug or adds a feature +- **perf**: A code change that improves performance +- **test**: Adding missing tests +- **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation + +You can add additional details after a new line to describe the change in detail or automatically close an issue on Github. + +```none +feat(CONTRIBUTING.md): create initial CONTRIBUTING.md + +makes the following wiki Page obsolete: +- https://github.com/Samagra-Development/Worksheet-Module/wiki/Reporting-bugs + +This closes #22 +``` + +### Developer Certificate of Origin (DCO) + +All commits not submitted via GitHub pull request shall contain a +Signed-off-by line, also known as the **Developer Certificate of Origin (DCO)** +as we know it from the Linux Kernel [Documenation/SubmittingPatches](https://www.kernel.org/doc/Documentation/process/submitting-patches.rst) + +```none + Signed-off-by: Peace Fun Ingenium +``` + +Additional tags in addition to Signed-off-by shall be used as long as it makes +sense for any commit, e.g. + +```none + Reviewed-by: + Tested-by: + Reviewed-by: + Suggested-by: + Acked-by: + Sponsored-by: +``` + +## Pull requests + +Pull requests with patches, improvements, new features are a great help. +Please keep them clean from unwanted commits. + +Follow the steps to get your work included in the project. + +1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, + and add the Worksheet-Module remote: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com//Worksheet-Module.git + # Navigate to the cloned directory + cd Worksheet-Module + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/Samagra-Development/Worksheet-Module.git + ``` + +2. Get the latest changes from upstream: + + ```bash + git checkout main + git pull upstream main + ``` + +3. Create a new branch from the main branch to contain your changes. + Best way is to call is to follow the type described in **Git Commit Conventions** + stated above: `/#/` + + ```bash + git checkout -b + ``` + + Example: + + ```bash + git checkout -b john/138/buckets-undefined-index + ``` + + Or + + ```bash + git checkout -b john/fix/138 + ``` + +4) It's coding time! + Please respect the coding convention: [Coding guidelines](https://github.com/Samagra-Development/Worksheet-Module/wiki) + + Commit your changes in logical chunks. Please adhere to **Git Commit Conventions** + and [Coding guidelines](https://github.com/Samagra-Development/Worksheet-Module/wiki) + or your code is unlikely to be merged into the main project. + Use Git's [interactive rebase](https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase) + feature to tidy up your commits before making them public. + +5) Locally rebase the upstream main branch into your topic branch: + + ```bash + git pull --rebase upstream main + ``` + +6) Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7) [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description against the `main` branch. diff --git a/packages/attendance/README.md b/packages/attendance/README.md new file mode 100644 index 00000000..01751cb7 --- /dev/null +++ b/packages/attendance/README.md @@ -0,0 +1,8 @@ +# Core Module +This modules containes common features used across Shiksha Apps e.g. + +## Exposed Components +| Module | Description | +| ----------- | ----------- | +| AppShell | Application Shell with header ,footer and navigation | +| ... | ... | \ No newline at end of file diff --git a/packages/attendance/craco.config.js b/packages/attendance/craco.config.js new file mode 100644 index 00000000..cdb4301c --- /dev/null +++ b/packages/attendance/craco.config.js @@ -0,0 +1,12 @@ +const cracoModuleFederation = require("craco-module-federation"); + +module.exports = { + devServer: { + port: 3002, + }, + plugins: [ + { + plugin: cracoModuleFederation, + }, + ], +}; diff --git a/packages/attendance/jsconfig.json b/packages/attendance/jsconfig.json new file mode 100644 index 00000000..5875dc5b --- /dev/null +++ b/packages/attendance/jsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "baseUrl": "src" + }, + "include": ["src"] +} diff --git a/packages/attendance/moduleFederation.config.js b/packages/attendance/moduleFederation.config.js new file mode 100644 index 00000000..dc89085a --- /dev/null +++ b/packages/attendance/moduleFederation.config.js @@ -0,0 +1,21 @@ +const { dependencies } = require("./package.json"); + +module.exports = { + name: "attendance", + exposes: { + "./App": "./src/App", + "./Attendance": "./src/pages/Attendance", + }, + filename: "moduleEntry.js", + shared: { + ...dependencies, + react: { + singleton: true, + requiredVersion: dependencies["react"], + }, + "react-dom": { + singleton: true, + requiredVersion: dependencies["react-dom"], + }, + }, +}; diff --git a/packages/attendance/package.json b/packages/attendance/package.json new file mode 100644 index 00000000..52e07f53 --- /dev/null +++ b/packages/attendance/package.json @@ -0,0 +1,98 @@ +{ + "name": "attendance", + "version": "0.1.0", + "private": true, + "dependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@mui/icons-material": "^5.2.4", + "@mui/material": "^5.4.0", + "@react-navigation/drawer": "^6.1.8", + "@shiksha/common-lib": "^1.0.0", + "@testing-library/jest-dom": "^5.16.2", + "@testing-library/react": "^11.2.7", + "@testing-library/user-event": "^12.8.3", + "axios": "^0.24.0", + "expo-font": "^10.0.3", + "i18next": "^21.6.7", + "moment": "^2.29.1", + "native-base": "^3.2.2", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-i18next": "^11.15.3", + "react-native-safe-area-context": "^3.3.2", + "react-native-tab-view": "^3.1.1", + "react-native-web": "^0.17.5", + "react-router-dom": "^6.1.1", + "react-scripts": "5.0.0", + "remixicon-react": "^1.0.0", + "web-vitals": "^0.2.4", + "workbox-background-sync": "^5.1.4", + "workbox-broadcast-update": "^5.1.4", + "workbox-cacheable-response": "^5.1.4", + "workbox-core": "^5.1.4", + "workbox-expiration": "^5.1.4", + "workbox-google-analytics": "^5.1.4", + "workbox-navigation-preload": "^5.1.4", + "workbox-precaching": "^5.1.4", + "workbox-range-requests": "^5.1.4", + "workbox-routing": "^5.1.4", + "workbox-strategies": "^5.1.4", + "workbox-streams": "^5.1.4" + }, + "scripts": { + "start": "craco start", + "build": "craco build", + "test": "react-scripts test", + "eject": "react-scripts eject", + "format": "prettier --write .", + "format:check": "prettier --check .", + "storybook": "start-storybook -p 6006 -s public", + "build-storybook": "build-storybook -s public" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ], + "overrides": [ + { + "files": [ + "**/*.stories.*" + ], + "rules": { + "import/no-anonymous-default-export": "off" + } + } + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@craco/craco": "^6.4.3", + "@storybook/addon-actions": "^6.4.19", + "@storybook/addon-essentials": "^6.4.19", + "@storybook/addon-interactions": "^6.4.19", + "@storybook/addon-links": "^6.4.19", + "@storybook/builder-webpack5": "^6.4.19", + "@storybook/manager-webpack5": "^6.4.19", + "@storybook/node-logger": "^6.4.19", + "@storybook/preset-create-react-app": "^4.0.1", + "@storybook/react": "^6.4.19", + "@storybook/testing-library": "^0.0.9", + "craco-module-federation": "^1.1.0", + "external-remotes-plugin": "^1.0.0", + "prettier": "^2.5.1", + "webpack": "^5.69.1" + } +} diff --git a/packages/attendance/public/favicon.ico b/packages/attendance/public/favicon.ico new file mode 100644 index 00000000..a11777cc Binary files /dev/null and b/packages/attendance/public/favicon.ico differ diff --git a/packages/attendance/public/index.html b/packages/attendance/public/index.html new file mode 100644 index 00000000..01fd3820 --- /dev/null +++ b/packages/attendance/public/index.html @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/packages/attendance/public/logo192.png b/packages/attendance/public/logo192.png new file mode 100644 index 00000000..fc44b0a3 Binary files /dev/null and b/packages/attendance/public/logo192.png differ diff --git a/packages/attendance/public/logo512.png b/packages/attendance/public/logo512.png new file mode 100644 index 00000000..a4e47a65 Binary files /dev/null and b/packages/attendance/public/logo512.png differ diff --git a/packages/attendance/public/manifest.json b/packages/attendance/public/manifest.json new file mode 100644 index 00000000..080d6c77 --- /dev/null +++ b/packages/attendance/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/packages/attendance/public/robots.txt b/packages/attendance/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/packages/attendance/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/packages/attendance/src/App.css b/packages/attendance/src/App.css new file mode 100644 index 00000000..74b5e053 --- /dev/null +++ b/packages/attendance/src/App.css @@ -0,0 +1,38 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/packages/attendance/src/App.js b/packages/attendance/src/App.js new file mode 100644 index 00000000..17aeb210 --- /dev/null +++ b/packages/attendance/src/App.js @@ -0,0 +1,27 @@ +import React from "react"; +import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; + +import "./App.css"; +import { extendTheme, NativeBaseProvider } from "native-base"; +import {DEFAULT_THEME} from '@shiksha/common-lib'; +import Attendance from "pages/Attendance"; + +function App() { + const theme = extendTheme(DEFAULT_THEME); + return ( + + + + + } /> + } /> + + } /> + + + + + ); +} + +export default App; diff --git a/packages/attendance/src/App.test.js b/packages/attendance/src/App.test.js new file mode 100644 index 00000000..d76787ed --- /dev/null +++ b/packages/attendance/src/App.test.js @@ -0,0 +1,9 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; + +test("renders learn react link", () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/packages/attendance/src/bootstrap.js b/packages/attendance/src/bootstrap.js new file mode 100644 index 00000000..5e5847c9 --- /dev/null +++ b/packages/attendance/src/bootstrap.js @@ -0,0 +1,23 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; +import * as serviceWorkerRegistration from "./serviceWorkerRegistration"; +import reportWebVitals from "./reportWebVitals"; + +ReactDOM.render( + + + , + document.getElementById("root") +); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://cra.link/PWA +serviceWorkerRegistration.unregister(); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/packages/attendance/src/components/AppShell.js b/packages/attendance/src/components/AppShell.js new file mode 100644 index 00000000..95bb563e --- /dev/null +++ b/packages/attendance/src/components/AppShell.js @@ -0,0 +1,8 @@ +function AppShell (){ + return ( + <> + Render shell here ... + + ) +} +export default AppShell; \ No newline at end of file diff --git a/packages/attendance/src/index.css b/packages/attendance/src/index.css new file mode 100644 index 00000000..4a1df4db --- /dev/null +++ b/packages/attendance/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} diff --git a/packages/attendance/src/index.js b/packages/attendance/src/index.js new file mode 100644 index 00000000..50599f7a --- /dev/null +++ b/packages/attendance/src/index.js @@ -0,0 +1 @@ +import("./bootstrap"); diff --git a/packages/attendance/src/logo.svg b/packages/attendance/src/logo.svg new file mode 100644 index 00000000..6b60c104 --- /dev/null +++ b/packages/attendance/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/attendance/src/manifest.json b/packages/attendance/src/manifest.json new file mode 100644 index 00000000..5ea4ff50 --- /dev/null +++ b/packages/attendance/src/manifest.json @@ -0,0 +1,99 @@ +{ + "@type": "samagraos.app.manifest", + "@version": "1", + "name": "Samagra Shiksha", + "short_name": "samagra-shiksha", + "theme_color": "#fff", + "background_color": "#fff", + "display": "browser", + "orientation": "portrait", + + "menus": { + "hamburger": [ + { + "title": "MY_CLASSES", + "icon": "email", + "module": "Registry", + "route": "/apps/registry/view", + "routeparameters": {} + }, + { + "title": "View Attendance", + "icon": "home", + "module": "Attendance", + "route": "/apps/attendance/view", + "routeparameters": {} + } + ], + "main": [ + { + "title": "MY_CLASSES", + "icon": "users", + "module": "Registry", + "route": "/classes", + "routeparameters": {} + } + ], + "footer": [ + { + "title": "HOME", + "icon": "Home4LineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + }, + { + "title": "CLASSES", + "icon": "TeamLineIcon", + "module": "Registry", + "route": "/classes", + "routeparameters": {} + }, + { + "title": "SCHOOL", + "icon": "GovernmentLineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + }, + { + "title": "MATERIALS", + "icon": "BookOpenLineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + }, + { + "title": "CAREER", + "icon": "UserLineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + } + ] + }, + "roles": [ + { "id": "school-admin", "title": "School Admin" }, + + { "id": "school-teacher", "title": "School Teacher" } + ], + + "config": [{}], + "languages": [ + { + "title": "English", + "path": "./samagra-shiksha.en-gb.json", + "overridePath": "./override-samagra-shiksha.en-gb.json", + "code": "en" + }, + { + "title": "Hindi", + "path": "./samagra-shiksha.hi-in.json", + "overridePath": "./override-samagra-shiksha.hi-in.json", + "code": "hi" + } + ], + "api_url": "https://dev-shiksha.uniteframework.io/registry/api/v1/", + "auth_url": "https://dev-shiksha.uniteframework.io/auth/realms/sunbird-rc/protocol/openid-connect/token", + "maxWidth": "1080" +} diff --git a/packages/attendance/src/pages/Attendance.js b/packages/attendance/src/pages/Attendance.js new file mode 100644 index 00000000..3364f19b --- /dev/null +++ b/packages/attendance/src/pages/Attendance.js @@ -0,0 +1,112 @@ +import { IconByName, Layout } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; +import * as moment from 'moment'; +import { useState } from "react"; +import { Link, useParams } from "react-router-dom"; +import { Box, HStack, Text, VStack } from "native-base"; + +export default function Attendance(){ + const { t } = useTranslation(); + const [weekPage, setWeekPage] = useState(0); + const [allAttendanceStatus, setAllAttendanceStatus] = useState({}); + const [students, setStudents] = useState([]); + const [searchStudents, setSearchStudents] = useState([]); + const [classObject, setClassObject] = useState({}); + const { classId } = useParams(); + const [loding, setLoding] = useState(false); + const teacherId = sessionStorage.getItem("id"); + const [attendance, setAttendance] = useState([]); + const [search, setSearch] = useState(); + + return ( + + + {t("REPORT")} + + + ), + }} + _appBar={{languages:['en']}} + subHeader={ + + + + + {classObject?.title ? classObject?.title : ""} + + + {t("TOTAL") + " " + students.length + " " + t("STUDENTS")} + + + + + + } + _subHeader={{ bg: "attendanceCard.500" }} + + _footer={{ + menues: [ + { + "title": "HOME", + "icon": "Home4LineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + }, + { + "title": "CLASSES", + "icon": "TeamLineIcon", + "module": "Registry", + "route": "/classes", + "routeparameters": {} + }, + { + "title": "SCHOOL", + "icon": "GovernmentLineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + }, + { + "title": "MATERIALS", + "icon": "BookOpenLineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + }, + { + "title": "CAREER", + "icon": "UserLineIcon", + "module": "Registry", + "route": "/", + "routeparameters": {} + } + ] + }} + > + +
Take Attendance
+ +
+ ); +} \ No newline at end of file diff --git a/packages/attendance/src/reportWebVitals.js b/packages/attendance/src/reportWebVitals.js new file mode 100644 index 00000000..9ecd33f9 --- /dev/null +++ b/packages/attendance/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = (onPerfEntry) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/packages/attendance/src/service-worker.js b/packages/attendance/src/service-worker.js new file mode 100644 index 00000000..a0d2c5e6 --- /dev/null +++ b/packages/attendance/src/service-worker.js @@ -0,0 +1,73 @@ +/* eslint-disable no-restricted-globals */ + +// This service worker can be customized! +// See https://developers.google.com/web/tools/workbox/modules +// for the list of available Workbox modules, or add any other +// code you'd like. +// You can also remove this file if you'd prefer not to use a +// service worker, and the Workbox build step will be skipped. + +import { clientsClaim } from "workbox-core"; +import { ExpirationPlugin } from "workbox-expiration"; +import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching"; +import { registerRoute } from "workbox-routing"; +import { StaleWhileRevalidate } from "workbox-strategies"; + +clientsClaim(); + +// Precache all of the assets generated by your build process. +// Their URLs are injected into the manifest variable below. +// This variable must be present somewhere in your service worker file, +// even if you decide not to use precaching. See https://cra.link/PWA +precacheAndRoute(self.__WB_MANIFEST); + +// Set up App Shell-style routing, so that all navigation requests +// are fulfilled with your index.html shell. Learn more at +// https://developers.google.com/web/fundamentals/architecture/app-shell +const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$"); +registerRoute( + // Return false to exempt requests from being fulfilled by index.html. + ({ request, url }) => { + // If this isn't a navigation, skip. + if (request.mode !== "navigate") { + return false; + } // If this is a URL that starts with /_, skip. + + if (url.pathname.startsWith("/_")) { + return false; + } // If this looks like a URL for a resource, because it contains // a file extension, skip. + + if (url.pathname.match(fileExtensionRegexp)) { + return false; + } // Return true to signal that we want to use the handler. + + return true; + }, + createHandlerBoundToURL(process.env.PUBLIC_URL + "/index.html") +); + +// An example runtime caching route for requests that aren't handled by the +// precache, in this case same-origin .png requests like those from in public/ +registerRoute( + // Add in any other file extensions or routing criteria as needed. + ({ url }) => + url.origin === self.location.origin && url.pathname.endsWith(".png"), // Customize this strategy as needed, e.g., by changing to CacheFirst. + new StaleWhileRevalidate({ + cacheName: "images", + plugins: [ + // Ensure that once this runtime cache reaches a maximum size the + // least-recently used images are removed. + new ExpirationPlugin({ maxEntries: 50 }), + ], + }) +); + +// This allows the web app to trigger skipWaiting via +// registration.waiting.postMessage({type: 'SKIP_WAITING'}) +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "SKIP_WAITING") { + self.skipWaiting(); + } +}); + +// Any other custom service worker logic can go here. diff --git a/packages/attendance/src/serviceWorkerRegistration.js b/packages/attendance/src/serviceWorkerRegistration.js new file mode 100644 index 00000000..fd0e8c5a --- /dev/null +++ b/packages/attendance/src/serviceWorkerRegistration.js @@ -0,0 +1,141 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// 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 subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://cra.link/PWA + +const isLocalhost = Boolean( + window.location.hostname === "localhost" || + // [::1] is the IPv6 localhost address. + window.location.hostname === "[::1]" || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + 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.href); + 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/facebook/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. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // 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://cra.link/PWA" + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then((registration) => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === "installed") { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + "New content is available and will be used when all " + + "tabs for this page are closed. See https://cra.link/PWA." + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } 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."); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch((error) => { + console.error("Error during service worker registration:", error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl, { + headers: { "Service-Worker": "script" }, + }) + .then((response) => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get("content-type"); + if ( + response.status === 404 || + (contentType != null && contentType.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, config); + } + }) + .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(); + }) + .catch((error) => { + console.error(error.message); + }); + } +} diff --git a/packages/attendance/src/setupTests.js b/packages/attendance/src/setupTests.js new file mode 100644 index 00000000..1dd407a6 --- /dev/null +++ b/packages/attendance/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import "@testing-library/jest-dom"; diff --git a/packages/attendance/src/stories/Button.jsx b/packages/attendance/src/stories/Button.jsx new file mode 100644 index 00000000..19c96568 --- /dev/null +++ b/packages/attendance/src/stories/Button.jsx @@ -0,0 +1,54 @@ +import React from "react"; +import PropTypes from "prop-types"; +import "./button.css"; + +/** + * Primary UI component for user interaction + */ +export const Button = ({ primary, backgroundColor, size, label, ...props }) => { + const mode = primary + ? "storybook-button--primary" + : "storybook-button--secondary"; + return ( + + ); +}; + +Button.propTypes = { + /** + * Is this the principal call to action on the page? + */ + primary: PropTypes.bool, + /** + * What background color to use + */ + backgroundColor: PropTypes.string, + /** + * How large should the button be? + */ + size: PropTypes.oneOf(["small", "medium", "large"]), + /** + * Button contents + */ + label: PropTypes.string.isRequired, + /** + * Optional click handler + */ + onClick: PropTypes.func, +}; + +Button.defaultProps = { + backgroundColor: null, + primary: false, + size: "medium", + onClick: undefined, +}; diff --git a/packages/attendance/src/stories/Button.stories.jsx b/packages/attendance/src/stories/Button.stories.jsx new file mode 100644 index 00000000..a391b602 --- /dev/null +++ b/packages/attendance/src/stories/Button.stories.jsx @@ -0,0 +1,40 @@ +import React from "react"; + +import { Button } from "./Button"; + +// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +export default { + title: "Example/Button", + component: Button, + // More on argTypes: https://storybook.js.org/docs/react/api/argtypes + argTypes: { + backgroundColor: { control: "color" }, + }, +}; + +// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args +const Template = (args) =>