Add files via upload

This commit is contained in:
alhan authored and GitHub committed 2022-07-05 12:24:54 +03:00
commit 382a413676
36 files changed
+31766

No files matched your search

View File
Whitespace-only changes.
+12
View File
@@ -0,0 +1,12 @@
{
"files": {
"main.css": "/static/css/main.9f0329a3.css",
"main.js": "/static/js/main.3f5e9df8.js",
"static/js/27.472c58b9.chunk.js": "/static/js/27.472c58b9.chunk.js",
"index.html": "/index.html"
},
"entrypoints": [
"static/css/main.9f0329a3.css",
"static/js/main.3f5e9df8.js"
]
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+1
View File
@@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.3f5e9df8.js"></script><link href="/static/css/main.9f0329a3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+25
View File
@@ -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"
}
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+1
View File
@@ -0,0 +1 @@
.box{background-color:aqua}.textField{margin:0!important}.jobListFilter{background-color:#daf0ff;padding-bottom:8px;padding-right:8px}.jobListFilterField{background-color:#fff}.jobListTitles{background-color:#bfe3fb;margin-top:0!important;padding-bottom:4px}.jobListLine{margin-top:8px}.jobListLine>div{background-color:#eee;border:1px solid #ccc;border-top:none}.jobListLine:nth-child(2n)>div{background-color:#fff}.priorityBox{border-radius:5px;color:#fff;font-size:14px;margin:0 auto 7px;max-width:100px;min-width:60px;padding:2px 0}.priority-Urgent{background-color:#ff4040}.priority-Regular{background-color:#ffb937}.priority-Trivial{background-color:#2f86ff}.footer{background-color:#dcebff;margin-left:-8px;padding:5px;text-align:right}.footer>span{font-size:12px;padding-right:5px}
+1
View File
@@ -0,0 +1 @@
"use strict";(self.webpackChunkrisetect_assessment=self.webpackChunkrisetect_assessment||[]).push([[27],{27:function(e,t,n){n.r(t),n.d(t,{getCLS:function(){return y},getFCP:function(){return g},getFID:function(){return C},getLCP:function(){return P},getTTFB:function(){return D}});var i,r,a,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},s=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},f=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},v=-1,p=function(){return"hidden"===document.visibilityState?0:1/0},d=function(){s((function(e){var t=e.timeStamp;v=t}),!0)},l=function(){return v<0&&(v=p(),d(),f((function(){setTimeout((function(){v=p(),d()}),0)}))),{get firstHiddenTime(){return v}}},g=function(e,t){var n,i=l(),r=u("FCP"),a=function(e){"first-contentful-paint"===e.name&&(s&&s.disconnect(),e.startTime<i.firstHiddenTime&&(r.value=e.startTime,r.entries.push(e),n(!0)))},o=window.performance&&performance.getEntriesByName&&performance.getEntriesByName("first-contentful-paint")[0],s=o?null:c("paint",a);(o||s)&&(n=m(e,r,t),o&&a(o),f((function(i){r=u("FCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,n(!0)}))}))})))},h=!1,T=-1,y=function(e,t){h||(g((function(e){T=e.value})),h=!0);var n,i=function(t){T>-1&&e(t)},r=u("CLS",0),a=0,o=[],v=function(e){if(!e.hadRecentInput){var t=o[0],i=o[o.length-1];a&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(a+=e.value,o.push(e)):(a=e.value,o=[e]),a>r.value&&(r.value=a,r.entries=o,n())}},p=c("layout-shift",v);p&&(n=m(i,r,t),s((function(){p.takeRecords().map(v),n(!0)})),f((function(){a=0,T=-1,r=u("CLS",0),n=m(i,r,t)})))},E={passive:!0,capture:!0},w=new Date,L=function(e,t){i||(i=t,r=e,a=new Date,F(removeEventListener),S())},S=function(){if(r>=0&&r<a-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+r};o.forEach((function(t){t(e)})),o=[]}},b=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){L(e,t),r()},i=function(){r()},r=function(){removeEventListener("pointerup",n,E),removeEventListener("pointercancel",i,E)};addEventListener("pointerup",n,E),addEventListener("pointercancel",i,E)}(t,e):L(t,e)}},F=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,b,E)}))},C=function(e,t){var n,a=l(),v=u("FID"),p=function(e){e.startTime<a.firstHiddenTime&&(v.value=e.processingStart-e.startTime,v.entries.push(e),n(!0))},d=c("first-input",p);n=m(e,v,t),d&&s((function(){d.takeRecords().map(p),d.disconnect()}),!0),d&&f((function(){var a;v=u("FID"),n=m(e,v,t),o=[],r=-1,i=null,F(addEventListener),a=p,o.push(a),S()}))},k={},P=function(e,t){var n,i=l(),r=u("LCP"),a=function(e){var t=e.startTime;t<i.firstHiddenTime&&(r.value=t,r.entries.push(e),n())},o=c("largest-contentful-paint",a);if(o){n=m(e,r,t);var v=function(){k[r.id]||(o.takeRecords().map(a),o.disconnect(),k[r.id]=!0,n(!0))};["keydown","click"].forEach((function(e){addEventListener(e,v,{once:!0,capture:!0})})),s(v,!0),f((function(i){r=u("LCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,k[r.id]=!0,n(!0)}))}))}))}},D=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0||n.value>performance.now())return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("load",(function(){return setTimeout(t,0)}))}}}]);
+2
View File
@@ -0,0 +1,2 @@
/*! For license information please see main.3f5e9df8.js.LICENSE.txt */
!function(){"use strict";var e={861:function(e,t,n){var r=n(456),i={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?a:l[e.$$typeof]||i}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=a;var u=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,h=Object.getPrototypeOf,p=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(p){var i=h(n);i&&i!==p&&e(t,i,r)}var a=c(n);d&&(a=a.concat(d(n)));for(var l=s(t),v=s(n),m=0;m<a.length;++m){var g=a[m];if(!o[g]&&(!r||!r[g])&&(!v||!v[g])&&(!l||!l[g])){var y=f(n,g);try{u(t,g,y)}catch(b){}}}}return t}},229:function(e,t){var n="function"===typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,i=n?Symbol.for("react.portal"):60106,o=n?Symbol.for("react.fragment"):60107,a=n?Symbol.for("react.strict_mode"):60108,l=n?Symbol.for("react.profiler"):60114,s=n?Symbol.for("react.provider"):60109,u=n?Symbol.for("react.context"):60110,c=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,f=n?Symbol.for("react.forward_ref"):60112,h=n?Symbol.for("react.suspense"):60113,p=n?Symbol.for("react.suspense_list"):60120,v=n?Symbol.for("react.memo"):60115,m=n?Symbol.for("react.lazy"):60116,g=n?Symbol.for("react.block"):60121,y=n?Symbol.for("react.fundamental"):60117,b=n?Symbol.for("react.responder"):60118,_=n?Symbol.for("react.scope"):60119;function k(e){if("object"===typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case c:case d:case o:case l:case a:case h:return e;default:switch(e=e&&e.$$typeof){case u:case f:case m:case v:case s:return e;default:return t}}case i:return t}}}function w(e){return k(e)===d}t.AsyncMode=c,t.ConcurrentMode=d,t.ContextConsumer=u,t.ContextProvider=s,t.Element=r,t.ForwardRef=f,t.Fragment=o,t.Lazy=m,t.Memo=v,t.Portal=i,t.Profiler=l,t.StrictMode=a,t.Suspense=h,t.isAsyncMode=function(e){return w(e)||k(e)===c},t.isConcurrentMode=w,t.isContextConsumer=function(e){return k(e)===u},t.isContextProvider=function(e){return k(e)===s},t.isElement=function(e){return"object"===typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return k(e)===f},t.isFragment=function(e){return k(e)===o},t.isLazy=function(e){return k(e)===m},t.isMemo=function(e){return k(e)===v},t.isPortal=function(e){return k(e)===i},t.isProfiler=function(e){return k(e)===l},t.isStrictMode=function(e){return k(e)===a},t.isSuspense=function(e){return k(e)===h},t.isValidElementType=function(e){return"string"===typeof e||"function"===typeof e||e===o||e===d||e===l||e===a||e===h||e===p||"object"===typeof e&&null!==e&&(e.$$typeof===m||e.$$typeof===v||e.$$typeof===s||e.$$typeof===u||e.$$typeof===f||e.$$typeof===y||e.$$typeof===b||e.$$typeof===_||e.$$typeof===g)},t.typeOf=k},456:function(e,t,n){e.exports=n(229)},534:function(e,t,n){var r=n(313),i=n(224);function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var a=new Set,l={};function s(e,t){u(e,t),u(e+"Capture",t)}function u(e,t){for(l[e]=t,e=0;e<t.length;e++)a.add(t[e])}var c=!("undefined"===typeof window||"undefined"===typeof window.document||"undefined"===typeof window.document.createElement),d=Object.prototype.hasOwnProperty,f=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,h={},p={};function v(e,t,n,r,i,o,a){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=i,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=a}var m={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(e){m[e]=new v(e,0,!1,e,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(e){var t=e[0];m[t]=new v(t,1,!1,e[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(e){m[e]=new v(e,2,!1,e.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(e){m[e]=new Line truncated
@@ -0,0 +1,133 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @license
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @license
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license MUI v5.8.0
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
+10
View File
@@ -0,0 +1,10 @@
{
"hosting": {
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
+30831
View File
File diff suppressed because it is too large. Load diff
+45
View File
@@ -0,0 +1,45 @@
{
"name": "risetect-assessment",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.9.3",
"@emotion/styled": "^11.9.3",
"@fontsource/roboto": "^4.5.7",
"@mui/icons-material": "^5.8.4",
"@mui/material": "^5.8.6",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"firebase": "^9.8.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.33.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "set \"GENERATE_SOURCEMAP=false\" && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+43
View File
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+25
View File
@@ -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"
}
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+40
View File
@@ -0,0 +1,40 @@
import { LensBlur } from '@mui/icons-material';
import ContextProvider from './utils/ContextProvider';
import JobList from './comps/JobList';
import CreateJob from './comps/CreateJob';
import { Container, Typography, Box, Grid, Divider } from '@mui/material';
import './mui/custom.css';
function App() {
return (
<Container maxWidth="md">
<Box sx={{ my: 4 }}>
<Grid container spacing={1}>
<Grid item>
<LensBlur fontSize="large" color="primary" />
</Grid>
<Grid item>
<Typography variant="h4" component="h2" gutterBottom color="primary">
Logo
</Typography>
</Grid>
</Grid>
<Divider sx={{ my: 2 }} />
<ContextProvider>
<CreateJob />
<Divider sx={{ my: 2 }} />
<JobList />
</ContextProvider>
<Box className='footer'>
<Typography variant="p" gutterBottom>
Alhan Özdemir @2022
</Typography>
</Box>
</Box>
</Container>
);
}
export default App;
+8
View File
@@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
+100
View File
@@ -0,0 +1,100 @@
import React, { Fragment } from "react";
import { Context } from '../utils/ContextProvider';
import { Button, Typography, TextField, Grid, FormControl, InputLabel, Select, MenuItem } from '@mui/material';
import { useForm, Controller } from 'react-hook-form';
export default function CreateJob(props) {
const { register, handleSubmit, reset, getValues, setValue, formState, control } = useForm({
mode: 'onChange',
defaultValues: {
name: '',
priority: '',
},
validationFields: ['priority']
});
const values = getValues();
return (
<Context.Consumer>
{context => (
<Fragment>
<Typography variant="h5" gutterBottom color="black">Create New Job</Typography>
<form onSubmit={handleSubmit((data) => {
context.saveData(data);
reset({
name: '',
priority: '',
});
})}>
<Grid container spacing={1}>
<Grid item md={8} xs={12}>
<Controller
name="name"
control={control}
defaultValue=""
render={({ field: { onChange, value }, fieldState: { error } }) => (
<TextField
fullWidth
label="Job Name"
size="small"
value={value}
onChange={onChange}
error={!!error}
helperText={error ? error.message : null}
{...register('name', {
pattern: {
value: /^[A-Za-z0-9 ıİüÜğĞçÇöÖşŞ]{1,255}$/,
message: 'Only Alphanumeric chars' // JS only: <p>error message</p> TS only support string
}
})}
{...register('name', { maxLength: { value: 255, message: "max 255 chars" } })}
/>
)}
rules={{ required: 'Job name required' }}
/>
</Grid>
<Grid item md={2} xs={6}>
<Controller
control={control}
name="priority"
render={({ field: { onChange, onBlur, value, ref }, formState }) => (
<FormControl fullWidth size="small">
<InputLabel id="demo-select-small">Priority</InputLabel>
<Select
fullWidth
labelId="demo-select-small"
id="demo-select-small"
label="Priority"
value={value}
defaultValue={values.priority}
onBlur={onBlur} // notify when input is touched
onChange={e => setValue('priority', e.target.value, true)}
{...register("priority", {
required: "Required",
})}
>
<MenuItem value="">SELECT ONE</MenuItem>
{Object.keys(context.priority).map(pid => (
<MenuItem key={pid} value={pid}>{context.priority[pid]}</MenuItem>
))}
</Select>
</FormControl>
)}
/>
</Grid>
<Grid item md={2} xs={6}>
<Button type="submit" variant="contained" fullWidth disabled={!formState.isValid}>CREATE</Button>
</Grid>
</Grid>
</form>
</Fragment>
)}
</Context.Consumer>
)
};
+62
View File
@@ -0,0 +1,62 @@
import { useState } from 'react';
import { Edit, Delete } from '@mui/icons-material';
import { Grid, IconButton, Box } from '@mui/material';
import DeleteDialog from '../mui/deleteDialog';
import EditDialog from '../mui/editDialog';
import '../mui/custom.css';
export default function Job(props) {
const [open, setOpen] = useState(false);
const [openEdit, setOpenEdit] = useState(false);
const handleClose = (value) => {
setOpen(false);
if (value !== false) {
props.deleteClick(value);
}
};
const handleCloseEdit = (value) => {
setOpenEdit(false);
if (value !== false) {
props.editClick(value);
}
};
return (
<Box className="jobListLine">
<DeleteDialog
open={open}
onClose={handleClose}
jobName={props.name}
jobId={props.jobId}
/>
<EditDialog
open={openEdit}
onClose={handleCloseEdit}
jobName={props.name}
jobPriority={props.priority}
jobId={props.jobId}
/>
<Grid container spacing={1}>
<Grid item md={8} sm={8} xs={6}>{props.name}</Grid>
<Grid item md={2} sm={2} xs={3} textAlign="center">
<Box className={"priorityBox priority-" + props.priority}>
{props.priority}
</Box>
</Grid>
<Grid item md={2} sm={2} xs={3} textAlign="right" paddingRight={1}>
<IconButton aria-label="edit" size="small"
onClick={(e) => setOpenEdit(true)} >
<Edit fontSize="inherit" />
</IconButton>
<IconButton aria-label="delete" size="small"
onClick={(e) => setOpen(true)} >
<Delete fontSize="inherit" />
</IconButton>
</Grid>
</Grid>
</Box>
);
}
+89
View File
@@ -0,0 +1,89 @@
import { Context } from '../utils/ContextProvider';
import Job from "./Job";
import { Typography, Box, Grid, TextField, Select, MenuItem, FormControl, InputLabel } from '@mui/material';
import { useContext, useState } from 'react';
import '../mui/custom.css';
export default function JobList() {
const [NameState, setNameState] = useState("");
const [PriorityState, setPriorityState] = useState("");
const context = useContext(Context);
const jobsAll = context.jobs;
const jobsSort = Object.values(jobsAll).sort(function (a, b) {
if (a.priority < b.priority) {
return -1;
} else if (a.priority === b.priority) {
if (a.name < b.name) return -1;
else return 1;
} else return 1;
});
const jobsFiltered = jobsSort.filter(item => PriorityState !== "" ?
item.priority.includes(PriorityState) : true)
.filter(item => NameState !== "" ? item.name.includes(NameState) : true);
return (
<Box sx={{ my: 2 }} >
<Typography variant="h5" gutterBottom color="black">Job List</Typography>
<Grid container spacing={1} className="jobListFilter">
<Grid item md={8} xs={9}>
<TextField
className='jobListFilterField'
fullWidth
id="outlined-name"
label="Name"
size="small"
value={NameState}
onChange={(e) => setNameState(e.target.value)}
/>
</Grid>
<Grid item md={4} xs={3} textAlign="center">
<FormControl fullWidth size="small">
<InputLabel id="demo-select-small">Priority</InputLabel>
<Select
className='jobListFilterField'
fullWidth
labelId="demo-select-small"
id="demo-select-small"
label="Priority"
value={PriorityState}
onChange={(e) => setPriorityState(e.target.value)}
>
<MenuItem value="">All</MenuItem>
{Object.keys(context.priority).map(pid => (
<MenuItem
key={pid}
value={pid}>
{context.priority[pid]}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</Grid>
<Grid container spacing={1} className='jobListTitles'>
<Grid item sm={8} xs={6}>
<Box>Name</Box>
</Grid>
<Grid item sm={2} xs={3} textAlign="center">Priority</Grid>
<Grid item sm={2} xs={3} textAlign="right" paddingRight={2}>Action</Grid>
</Grid>
{jobsFiltered.map(jobID => (
<Job
key={jobID.id}
jobId={jobID.id}
name={jobID.name}
priority={context.priority[jobID.priority]}
editClick={(e) => context.editClick(e)}
deleteClick={(e) => context.deleteClick(jobID.id)}
/>
))}
</Box>
);
}
+22
View File
@@ -0,0 +1,22 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';
import theme from './mui/theme';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
);
// 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();
+55
View File
@@ -0,0 +1,55 @@
.box{
background-color: aqua;
}
.textField{
margin: 0 !important;
}
.jobListFilter{
background-color:#daf0ff;
padding-bottom: 8px;
padding-right: 8px;
}
.jobListFilterField{
background-color:#ffffff;
}
.jobListTitles{
background-color: #bfe3fb;
margin-top: 0px!important;
padding-bottom: 4px;
}
.jobListLine{
margin-top:8px;
}
.jobListLine > div{
background-color:#eee;
border: 1px solid #ccc;
border-top: none;
}
.jobListLine:nth-child(2n) > div{
background-color:#ffffff;
}
.priorityBox{
max-width: 100px;
min-width: 60px;
margin:0 auto 7px;
border-radius: 5px;
color: #fff;
font-size: 14px;
padding: 2px 0;
}
.priority-Urgent{
background-color: rgb(255, 64, 64);
}
.priority-Regular{
background-color: rgb(255, 185, 55);
}
.priority-Trivial{
background-color: rgb(47, 134, 255);
}
.footer{
background-color: rgb(220 235 255);
text-align: right;
margin-left: -8px;
padding: 5px;
}
.footer > span{ font-size: 12px; padding-right: 5px;}
+37
View File
@@ -0,0 +1,37 @@
import PropTypes from 'prop-types';
import { Report } from '@mui/icons-material';
import { Dialog, Button, Stack, Typography } from '@mui/material';
export default function DeleteDialog(props) {
const { onClose, open, jobName, jobId } = props;
const handleClose = () => {
onClose(false);
};
const handleApprove = (value) => {
onClose(value);
};
return (
<Dialog onClose={handleClose} open={open} maxWidth={"xs"} fullWidth>
<Typography variant='h4' m={0} mt={2} textAlign="center">Delete Job</Typography>
<Typography variant='p' m={1} textAlign="center">{jobName}</Typography>
<Stack alignItems="center">
<Report fontSize='large' color='error' />
</Stack>
<Stack direction="row" spacing={5} alignSelf={"center"} padding={3}>
<Button variant="contained" fullWidth
onClick={() => handleClose()}>Cancel</Button>
<Button variant="contained" color='error' fullWidth
onClick={() => handleApprove(jobId)}>Approve</Button>
</Stack>
</Dialog>
);
}
DeleteDialog.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
jobName: PropTypes.string.isRequired,
jobId: PropTypes.string.isRequired,
};
+109
View File
@@ -0,0 +1,109 @@
import PropTypes from 'prop-types';
import { Context } from '../utils/ContextProvider';
import { Dialog, DialogTitle, Button, Stack, TextField, InputLabel, FormControl, Select, MenuItem } from '@mui/material';
import { useForm, Controller } from 'react-hook-form';
export default function EditDialog(props) {
const { onClose, open, jobName, jobPriority, jobId } = props;
const { register, handleSubmit, getValues, setValue, formState, control } = useForm({
mode: 'onChange',
defaultValues: {
name: jobName,
priority: jobPriority,
},
validationFields: ['priority']
});
const values = getValues();
const handleClose = () => {
onClose(false);
};
const handleSave = (value) => {
onClose(value);
};
return (
<Dialog onClose={handleClose} open={open} maxWidth={"xs"} fullWidth>
<DialogTitle textAlign={"center"}>Edit Job</DialogTitle>
<form onSubmit={handleSubmit((data) => {
handleSave({ id: jobId, data: data });
})}>
<Stack direction="column" spacing={3} alignSelf={"center"} paddingX={3} >
<Controller
name="name"
padding={3}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<TextField
fullWidth
label="Job Name"
size="small"
disabled
value={formState.name}
onChange={onChange}
error={!!error}
helperText={error ? error.message : null}
{...register('name', {
pattern: {
value: /^[A-Za-z0-9 ıİüÜğĞçÇöÖşŞ]{1,255}$/,
message: 'Only Alphanumeric chars'
}
})}
{...register('name', { maxLength: { value: 255, message: "max 255 chars" } })}
/>
)}
rules={{ required: 'Job name required' }}
/>
<Context.Consumer>
{context => (
<Controller
control={control}
name="priority"
render={({ field: { onChange, onBlur, value, ref }, formState }) => (
<FormControl fullWidth size="small">
<InputLabel id="demo-select-small">Priority</InputLabel>
<Select
fullWidth
labelId="demo-select-small"
id="demo-select-small"
label="Priority"
value={value}
defaultValue={values.priority}
onBlur={onBlur}
onChange={e => setValue('priority', e.target.value, true)}
{...register("priority", {
required: "Required",
})}
>
{Object.keys(context.priority).map(pid => (
<MenuItem key={pid} value={context.priority[pid]}>{context.priority[pid]}</MenuItem>
))}
</Select>
</FormControl>
)}
/>
)}
</Context.Consumer>
</Stack>
<Stack direction="row" spacing={4} justifyContent="center" padding={4} >
<Button variant="contained" size='small' fullWidth
onClick={() => handleClose()}>Cancel</Button>
<Button type="submit" size="small" variant="contained" color='error' fullWidth>Save</Button>
</Stack>
</form>
</Dialog>
);
}
EditDialog.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
jobName: PropTypes.string.isRequired,
jobPriority: PropTypes.string.isRequired,
jobId: PropTypes.string.isRequired,
};
+18
View File
@@ -0,0 +1,18 @@
import { red } from '@mui/material/colors';
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: '#556cd6',
},
secondary: {
main: '#19857b',
},
error: {
main: red.A400,
},
},
});
export default theme;
+13
View File
@@ -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;
+5
View File
@@ -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';
+52
View File
@@ -0,0 +1,52 @@
import React, { useState } from 'react';
import { ref, set, onValue } from "firebase/database";
import { db } from "./firebase";
export const Context = React.createContext();
export default function ContextProvider(props) {
const [JobsState, setJobsState] = useState(null);
const priority = ["Urgent", "Regular", "Trivial"];
const initDB = JobsState === null ? getDB() : true;
if (!initDB) return (<div className='loading'>loading</div>);
function getDB() {
onValue(ref(db, "jobs/"), (snapshot) => {
if (snapshot.val())
setJobsState(snapshot.val());
else
setJobsState("");
});
return false;
}
return (
<Context.Provider
value={{
jobs: JobsState,
priority: priority,
saveData: (data) => {
const rnd = Date.now() + "-" + Math.floor(Math.random() * 1000000000).toString();
const record = {
id: rnd,
name: data.name,
priority: data.priority
}
set(ref(db, "jobs/" + rnd), record);
},
editClick: (v) => {
//console.log(v);
set(ref(db, "jobs/" + v.id), v.data);
},
deleteClick: (jobId) => {
//console.log(jobId);
set(ref(db, "jobs/" + jobId), null);
}
}}
>
{props.children}
</Context.Provider>
);
}
+16
View File
@@ -0,0 +1,16 @@
import { initializeApp } from 'firebase/app';
import { getDatabase } from "firebase/database";
const firebaseConfig = {
apiKey: "xxx",
authDomain: "risetech-assessment.firebaseapp.com",
databaseURL: "https://risetech-assessment-default-rtdb.europe-west1.firebasedatabase.app",
projectId: "risetech-assessment",
storageBucket: "risetech-assessment.appspot.com",
messagingSenderId: "xxx",
appId: "xxx"
};
const app = initializeApp(firebaseConfig);
export const db = getDatabase(app);
+5
View File
@@ -0,0 +1,5 @@
export const reverseArray = arr => {
let newArr = [];
for (let i = arr.length - 1; i >= 0; i--) newArr.push(arr[i]);
return newArr;
}