create: new folder
This commit is contained in:
parent
3d7f92c177
commit
3990ecc391
BIN
Laporan_Skripsi_Komang Gede Narariya Suputra.docx
Normal file
BIN
Laporan_Skripsi_Komang Gede Narariya Suputra.docx
Normal file
Binary file not shown.
BIN
Manual Program/RUII2.docx
Normal file
BIN
Manual Program/RUII2.docx
Normal file
Binary file not shown.
BIN
Manual Program/RUII3.docx
Normal file
BIN
Manual Program/RUII3.docx
Normal file
Binary file not shown.
BIN
Manual Program/RUII4.docx
Normal file
BIN
Manual Program/RUII4.docx
Normal file
Binary file not shown.
BIN
Manual Program/RUII5.docx
Normal file
BIN
Manual Program/RUII5.docx
Normal file
Binary file not shown.
BIN
Manual Program/RUII6.docx
Normal file
BIN
Manual Program/RUII6.docx
Normal file
Binary file not shown.
BIN
Persentasi Semhas React.js.pptx
Normal file
BIN
Persentasi Semhas React.js.pptx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII1.docx
Normal file
BIN
Reactjs1/Modul/RUII1.docx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII1.pdf
Normal file
BIN
Reactjs1/Modul/RUII1.pdf
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII2.docx
Normal file
BIN
Reactjs1/Modul/RUII2.docx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII2.pdf
Normal file
BIN
Reactjs1/Modul/RUII2.pdf
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII3.docx
Normal file
BIN
Reactjs1/Modul/RUII3.docx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII3.pdf
Normal file
BIN
Reactjs1/Modul/RUII3.pdf
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII4.docx
Normal file
BIN
Reactjs1/Modul/RUII4.docx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII4.pdf
Normal file
BIN
Reactjs1/Modul/RUII4.pdf
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII5.docx
Normal file
BIN
Reactjs1/Modul/RUII5.docx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII5.pdf
Normal file
BIN
Reactjs1/Modul/RUII5.pdf
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII6.docx
Normal file
BIN
Reactjs1/Modul/RUII6.docx
Normal file
Binary file not shown.
BIN
Reactjs1/Modul/RUII6.pdf
Normal file
BIN
Reactjs1/Modul/RUII6.pdf
Normal file
Binary file not shown.
23
Reactjs1/my-app/.gitignore
vendored
Normal file
23
Reactjs1/my-app/.gitignore
vendored
Normal file
|
|
@ -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*
|
||||||
BIN
Reactjs1/my-app/Modul Pembelajran/RUII1.docx
Normal file
BIN
Reactjs1/my-app/Modul Pembelajran/RUII1.docx
Normal file
Binary file not shown.
BIN
Reactjs1/my-app/Modul Pembelajran/RUII2.docx
Normal file
BIN
Reactjs1/my-app/Modul Pembelajran/RUII2.docx
Normal file
Binary file not shown.
BIN
Reactjs1/my-app/Modul Pembelajran/RUII3.docx
Normal file
BIN
Reactjs1/my-app/Modul Pembelajran/RUII3.docx
Normal file
Binary file not shown.
BIN
Reactjs1/my-app/Modul Pembelajran/RUII4.docx
Normal file
BIN
Reactjs1/my-app/Modul Pembelajran/RUII4.docx
Normal file
Binary file not shown.
BIN
Reactjs1/my-app/Modul Pembelajran/RUII5.docx
Normal file
BIN
Reactjs1/my-app/Modul Pembelajran/RUII5.docx
Normal file
Binary file not shown.
70
Reactjs1/my-app/README.md
Normal file
70
Reactjs1/my-app/README.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||||
|
|
||||||
|
The page will reload when you make changes.\
|
||||||
|
You may also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||||
|
|
||||||
|
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||||
|
|
||||||
|
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||||
|
|
||||||
|
### Analyzing the Bundle Size
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||||
|
|
||||||
|
### Making a Progressive Web App
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||||
|
|
||||||
|
### `npm run build` fails to minify
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||||
32221
Reactjs1/my-app/package-lock.json
generated
Normal file
32221
Reactjs1/my-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
Reactjs1/my-app/package.json
Normal file
48
Reactjs1/my-app/package.json
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"name": "my-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"diff": "^5.2.0",
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"mathjs": "^12.4.2",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-router-dom": "^6.23.1",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"react-test-renderer": "^18.3.1",
|
||||||
|
"sweetalert2": "^11.11.0",
|
||||||
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "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"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
|
"@testing-library/react": "^15.0.7",
|
||||||
|
"jest": "^27.5.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Reactjs1/my-app/public/favicon.ico
Normal file
BIN
Reactjs1/my-app/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
BIN
Reactjs1/my-app/public/images/React.gif
Normal file
BIN
Reactjs1/my-app/public/images/React.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
43
Reactjs1/my-app/public/index.html
Normal file
43
Reactjs1/my-app/public/index.html
Normal 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>
|
||||||
BIN
Reactjs1/my-app/public/logo192.png
Normal file
BIN
Reactjs1/my-app/public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
Reactjs1/my-app/public/logo512.png
Normal file
BIN
Reactjs1/my-app/public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
25
Reactjs1/my-app/public/manifest.json
Normal file
25
Reactjs1/my-app/public/manifest.json
Normal 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
Reactjs1/my-app/public/robots.txt
Normal file
3
Reactjs1/my-app/public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
29
Reactjs1/my-app/src/App.2js
Normal file
29
Reactjs1/my-app/src/App.2js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import HelloWord from "./components/helloword/Hello";
|
||||||
|
// import Form from './components/form/Form';
|
||||||
|
// import {Count} from './components/count/Count';
|
||||||
|
// import LoginForm from './components/form/LoginForm';
|
||||||
|
// import TestCase2 from "./components/form/TestCase2"
|
||||||
|
// import Input from "./components/input/Input";
|
||||||
|
// import { Counter } from "./components/counter/Counter";
|
||||||
|
// import Home from "./components/websitereact/Home";
|
||||||
|
// import Login from "./components/websitereact/Login";``
|
||||||
|
// import Testcase3 from './components/websitereact/Testcase3';
|
||||||
|
// import Login from "./components/websitereact/Login";
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
< HelloWord/>
|
||||||
|
{/* <Form /> */}
|
||||||
|
{/* <Count /> */}
|
||||||
|
{/* <LoginForm /> */}
|
||||||
|
{/* <TestCase2 /> */}
|
||||||
|
{/* <Input/> */}
|
||||||
|
{/* <Counter /> */}
|
||||||
|
{/* <Home/> */}
|
||||||
|
{/* <Testcase3 /> */}
|
||||||
|
{/* <Login/> */}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
38
Reactjs1/my-app/src/App.css
Normal file
38
Reactjs1/my-app/src/App.css
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Reactjs1/my-app/src/App.js
Normal file
22
Reactjs1/my-app/src/App.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||||
|
import Home from './components/websitereact/Home';
|
||||||
|
import Login from './components/websitereact/Login';
|
||||||
|
import Calculator from './components/websitereact/Calculator';
|
||||||
|
import Todolist from './components/websitereact/Todolist';
|
||||||
|
import Navbar from './components/websitereact/Navbar';
|
||||||
|
const App = () => {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Login />} />
|
||||||
|
<Route path="/Home" element={<Home />} />
|
||||||
|
<Route path="/Todolist" element={<Todolist />} />
|
||||||
|
<Route path="/Calculator" element={<Calculator />} />
|
||||||
|
<Route path="/Navbar" element={<Navbar />} />
|
||||||
|
</Routes>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
125
Reactjs1/my-app/src/components/__test__/Counter.test.js
Normal file
125
Reactjs1/my-app/src/components/__test__/Counter.test.js
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import { diffLines } from 'diff';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { Counter } from '../counter/Counter';
|
||||||
|
|
||||||
|
// Fungsi untuk membaca file
|
||||||
|
const readFileContent = (filePath) => {
|
||||||
|
return fs.readFileSync(path.resolve(filePath), 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fungsi untuk membandingkan dua string kode
|
||||||
|
const compareFiles = (studentCode, answerKeyCode) => {
|
||||||
|
const differences = diffLines(studentCode, answerKeyCode);
|
||||||
|
const hasDifferences = differences.some(part => part.added || part.removed);
|
||||||
|
|
||||||
|
if (hasDifferences) {
|
||||||
|
differences.forEach((part, index) => {
|
||||||
|
if (part.added || part.removed) {
|
||||||
|
console.log(`Difference at line ${index + 1}: ${part.value}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Path file mahasiswa dan kunci jawaban
|
||||||
|
const studentFilePath = './src/components/irbaAdika/Counter.js';
|
||||||
|
const answerKeyFilePath = './src/components/kunci/Counter.js';
|
||||||
|
|
||||||
|
// Baca konten file
|
||||||
|
const studentCode = readFileContent(studentFilePath);
|
||||||
|
const answerKeyCode = readFileContent(answerKeyFilePath);
|
||||||
|
|
||||||
|
// Bandingkan file sebelum menjalankan pengujian
|
||||||
|
if (compareFiles(studentCode, answerKeyCode)) {
|
||||||
|
console.log('✔ Kode sesuai dengan kunci jawaban. Melanjutkan ke pengujian otomatis...');
|
||||||
|
|
||||||
|
// Define Jest tests
|
||||||
|
describe('Counter component', () => {
|
||||||
|
test('renders correctly', () => {
|
||||||
|
render(<Counter />);
|
||||||
|
expect(screen.getByRole('heading')).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'Decrement' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('spinbutton')).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'Set' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'Increment' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'Reset' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 0', () => {
|
||||||
|
render(<Counter />);
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of -1 after clicking the decrement button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Decrement' }));
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of -2 after clicking the decrement button twice', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Decrement' }));
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Decrement' }));
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 10 after clicking the set button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
await userEvent.type(screen.getByRole('spinbutton'), '10');
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Set' }));
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('10');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 1 after clicking the increment button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Increment' }));
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 2 after clicking the increment button twice', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Increment' }));
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Increment' }));
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 0 after clicking the reset button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Increment' }));
|
||||||
|
await userEvent.click(screen.getByRole('button', { name: 'Reset' }));
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('elements are focused in the right order', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const decrementButton = screen.getByRole('button', { name: 'Decrement' });
|
||||||
|
const amountInput = screen.getByRole('spinbutton');
|
||||||
|
const setButton = screen.getByRole('button', { name: 'Set' });
|
||||||
|
const incrementButton = screen.getByRole('button', { name: 'Increment' });
|
||||||
|
const resetButton = screen.getByRole('button', { name: 'Reset' });
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(document.activeElement).toBe(decrementButton);
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(document.activeElement).toBe(amountInput);
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(document.activeElement).toBe(setButton);
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(document.activeElement).toBe(incrementButton);
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(document.activeElement).toBe(resetButton);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ Kode tidak sesuai dengan kunci jawaban.');
|
||||||
|
}
|
||||||
176
Reactjs1/my-app/src/components/__test__/Navbar.test.js
Normal file
176
Reactjs1/my-app/src/components/__test__/Navbar.test.js
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
import { diffLines } from 'diff';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import Navbar from '../websitereact/Navbar';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
jest.mock('sweetalert2', () => ({
|
||||||
|
fire: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockNavigate = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useNavigate: () => mockNavigate,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Fungsi untuk membaca isi file
|
||||||
|
const readFileContent = (filePath) => {
|
||||||
|
return fs.readFileSync(path.resolve(filePath), 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fungsi untuk membandingkan dua string kode
|
||||||
|
const compareFiles = (studentCode, answerKeyCode) => {
|
||||||
|
const differences = diffLines(studentCode, answerKeyCode);
|
||||||
|
const hasDifferences = differences.some(part => part.added || part.removed);
|
||||||
|
|
||||||
|
if (hasDifferences) {
|
||||||
|
differences.forEach((part, index) => {
|
||||||
|
if (part.added || part.removed) {
|
||||||
|
console.log(`Difference at line ${index + 1}: ${part.value}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Path file komponen mahasiswa dan kunci jawaban
|
||||||
|
const studentFilePath = './src/components/irbaAdika/Navbar.js';
|
||||||
|
const answerKeyFilePath = './src/components/kunci/Navbar.js';
|
||||||
|
|
||||||
|
// Baca isi file
|
||||||
|
const studentCode = readFileContent(studentFilePath);
|
||||||
|
const answerKeyCode = readFileContent(answerKeyFilePath);
|
||||||
|
|
||||||
|
// Bandingkan file sebelum menjalankan pengujian
|
||||||
|
if (compareFiles(studentCode, answerKeyCode)) {
|
||||||
|
console.log('✔ Kode sesuai dengan kunci jawaban. Melanjutkan ke pengujian otomatis...');
|
||||||
|
|
||||||
|
describe('Navbar', () => {
|
||||||
|
test('renders correctly', () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<Navbar />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
const homeLink = screen.getByRole('link', { name: 'Home' });
|
||||||
|
expect(homeLink).toBeInTheDocument();
|
||||||
|
|
||||||
|
const todolistLink = screen.getByRole('link', { name: 'Todolist' });
|
||||||
|
expect(todolistLink).toBeInTheDocument();
|
||||||
|
|
||||||
|
const calculatorLink = screen.getByRole('link', { name: 'Calculator' });
|
||||||
|
expect(calculatorLink).toBeInTheDocument();
|
||||||
|
|
||||||
|
const logoutButton = screen.getByRole('button', { name: 'Logout' });
|
||||||
|
expect(logoutButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows logout confirmation on logout button click', async () => {
|
||||||
|
Swal.fire.mockResolvedValueOnce({ isConfirmed: true });
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<Navbar />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
const logoutButton = screen.getByRole('button', { name: 'Logout' });
|
||||||
|
await userEvent.click(logoutButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(Swal.fire).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
title: 'Logout',
|
||||||
|
text: 'Apakah Anda yakin ingin logout?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya',
|
||||||
|
cancelButtonText: 'Tidak',
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('navigates to home on confirm logout', async () => {
|
||||||
|
Swal.fire.mockResolvedValueOnce({ isConfirmed: true });
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<Navbar />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
const logoutButton = screen.getByRole('button', { name: 'Logout' });
|
||||||
|
await userEvent.click(logoutButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(Swal.fire).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
title: 'Logout',
|
||||||
|
text: 'Apakah Anda yakin ingin logout?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya',
|
||||||
|
cancelButtonText: 'Tidak',
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(Swal.fire).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
title: 'Logged Out',
|
||||||
|
text: 'You have been successfully logged out.',
|
||||||
|
icon: 'info',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: expect.any(Function),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const willCloseCallback = Swal.fire.mock.calls[1][0].willClose;
|
||||||
|
willCloseCallback();
|
||||||
|
|
||||||
|
expect(mockNavigate).toHaveBeenCalledWith('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not navigate to home on cancel logout', async () => {
|
||||||
|
Swal.fire.mockResolvedValueOnce({ isConfirmed: false });
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<Navbar />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
const logoutButton = screen.getByRole('button', { name: 'Logout' });
|
||||||
|
await userEvent.click(logoutButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(Swal.fire).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
title: 'Logout',
|
||||||
|
text: 'Apakah Anda yakin ingin logout?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya',
|
||||||
|
cancelButtonText: 'Tidak',
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Swal.fire).not.toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
title: 'Logged Out',
|
||||||
|
text: 'You have been successfully logged out.',
|
||||||
|
icon: 'info',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(mockNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ Kode tidak sesuai dengan kunci jawaban.');
|
||||||
|
}
|
||||||
88
Reactjs1/my-app/src/components/__test__/TestCase2.test.js
Normal file
88
Reactjs1/my-app/src/components/__test__/TestCase2.test.js
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
import user from '@testing-library/user-event';
|
||||||
|
import TestCase2 from '../form/TestCase2';
|
||||||
|
import assert from 'assert';
|
||||||
|
import { diffLines } from 'diff';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
// Fungsi untuk membaca file
|
||||||
|
const readFileContent = (filePath) => {
|
||||||
|
return fs.readFileSync(path.resolve(filePath), 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fungsi untuk membandingkan dua string kode
|
||||||
|
const compareFiles = (studentCode, answerKeyCode) => {
|
||||||
|
const differences = diffLines(studentCode, answerKeyCode);
|
||||||
|
const hasDifferences = differences.some(part => part.added || part.removed);
|
||||||
|
|
||||||
|
if (hasDifferences) {
|
||||||
|
differences.forEach((part, index) => {
|
||||||
|
if (part.added || part.removed) {
|
||||||
|
console.log(`Difference at line ${index + 1}: ${part.value}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Path file mahasiswa dan kunci jawaban
|
||||||
|
const studentFilePath = './src/components/irbaAdika/Form.js';
|
||||||
|
const answerKeyFilePath = './src/components/kunci/Form.js';
|
||||||
|
|
||||||
|
// Baca konten file
|
||||||
|
const studentCode = readFileContent(studentFilePath);
|
||||||
|
const answerKeyCode = readFileContent(answerKeyFilePath);
|
||||||
|
|
||||||
|
// Bandingkan file sebelum menjalankan pengujian
|
||||||
|
if (compareFiles(studentCode, answerKeyCode)) {
|
||||||
|
console.log('✔ Kode sesuai dengan kunci jawaban. Melanjutkan ke pengujian otomatis...');
|
||||||
|
|
||||||
|
// Define Jest tests
|
||||||
|
describe('TestCase2', () => {
|
||||||
|
test('renders username and password inputs correctly', () => {
|
||||||
|
render(<TestCase2 />);
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
assert.ok(usernameInput, 'Username input should be rendered');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
assert.ok(passwordInput, 'Password input should be rendered');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login fails with incorrect credentials', async () => {
|
||||||
|
const originalAlert = window.alert;
|
||||||
|
window.alert = jest.fn();
|
||||||
|
|
||||||
|
render(<TestCase2 />);
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: 'Login' });
|
||||||
|
|
||||||
|
await user.type(usernameInput, 'wrongusername');
|
||||||
|
await user.type(passwordInput, 'wrongpassword');
|
||||||
|
await user.click(loginButton);
|
||||||
|
|
||||||
|
assert.strictEqual(screen.queryByText('Halo, wrongusername!'), null, 'Username greeting should not be displayed');
|
||||||
|
assert.strictEqual(window.alert.mock.calls.length, 1, 'Alert should be called once');
|
||||||
|
assert.strictEqual(window.alert.mock.calls[0][0], 'Login gagal.', 'Alert message should be "Login gagal."');
|
||||||
|
|
||||||
|
window.alert = originalAlert;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login succeeds with correct credentials', async () => {
|
||||||
|
render(<TestCase2 />);
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: 'Login' });
|
||||||
|
|
||||||
|
await user.type(usernameInput, 'admin');
|
||||||
|
await user.type(passwordInput, 'password');
|
||||||
|
await user.click(loginButton);
|
||||||
|
|
||||||
|
assert.ok(screen.getByText('Halo, admin!'), 'Successful login message should be displayed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ Kode tidak sesuai dengan kunci jawaban.');
|
||||||
|
}
|
||||||
132
Reactjs1/my-app/src/components/__test__/TestCase3.test.js
Normal file
132
Reactjs1/my-app/src/components/__test__/TestCase3.test.js
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
import { diffLines } from 'diff';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { render, screen, act } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import TestCase3 from '../websitereact/TestCase3';
|
||||||
|
|
||||||
|
jest.mock('sweetalert2', () => ({
|
||||||
|
fire: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Fungsi untuk membaca isi file
|
||||||
|
const readFileContent = (filePath) => {
|
||||||
|
return fs.readFileSync(path.resolve(filePath), 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fungsi untuk membandingkan dua string kode
|
||||||
|
const compareFiles = (studentCode, answerKeyCode) => {
|
||||||
|
const differences = diffLines(studentCode, answerKeyCode);
|
||||||
|
const hasDifferences = differences.some(part => part.added || part.removed);
|
||||||
|
|
||||||
|
if (hasDifferences) {
|
||||||
|
differences.forEach((part, index) => {
|
||||||
|
if (part.added || part.removed) {
|
||||||
|
console.log(`Difference at line ${index + 1}: ${part.value}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Path file komponen mahasiswa dan kunci jawaban
|
||||||
|
const studentFilePath = './src/components/irbaAdika/FormStyle.js';
|
||||||
|
const answerKeyFilePath = './src/components/kunci/FormStyle.js';
|
||||||
|
|
||||||
|
// Baca isi file
|
||||||
|
const studentCode = readFileContent(studentFilePath);
|
||||||
|
const answerKeyCode = readFileContent(answerKeyFilePath);
|
||||||
|
|
||||||
|
// Bandingkan file sebelum menjalankan pengujian
|
||||||
|
if (compareFiles(studentCode, answerKeyCode)) {
|
||||||
|
console.log('✔ Kode sesuai dengan kunci jawaban. Melanjutkan ke pengujian otomatis...');
|
||||||
|
|
||||||
|
// Define Jest tests
|
||||||
|
describe('Login Component', () => {
|
||||||
|
test('renders input fields, show password checkbox, and login button', () => {
|
||||||
|
render(<TestCase3 />);
|
||||||
|
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const showPasswordCheckbox = screen.getByLabelText('Show Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: /login/i });
|
||||||
|
|
||||||
|
expect(usernameInput).toBeInTheDocument();
|
||||||
|
expect(passwordInput).toBeInTheDocument();
|
||||||
|
expect(showPasswordCheckbox).toBeInTheDocument();
|
||||||
|
expect(loginButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toggles password visibility when show password checkbox is clicked', async () => {
|
||||||
|
render(<TestCase3 />);
|
||||||
|
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const showPasswordCheckbox = screen.getByLabelText('Show Password');
|
||||||
|
|
||||||
|
expect(passwordInput).toHaveAttribute('type', 'password');
|
||||||
|
await userEvent.click(showPasswordCheckbox);
|
||||||
|
|
||||||
|
expect(passwordInput).toHaveAttribute('type', 'text');
|
||||||
|
await userEvent.click(showPasswordCheckbox);
|
||||||
|
|
||||||
|
expect(passwordInput).toHaveAttribute('type', 'password');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows success alert on successful login', async () => {
|
||||||
|
render(<TestCase3 />);
|
||||||
|
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: /login/i });
|
||||||
|
|
||||||
|
await userEvent.type(usernameInput, 'React');
|
||||||
|
await userEvent.type(passwordInput, 'password');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.click(loginButton);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1100));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Swal.fire).toHaveBeenCalled();
|
||||||
|
|
||||||
|
const [[callArgs]] = Swal.fire.mock.calls;
|
||||||
|
|
||||||
|
expect(callArgs).toEqual(expect.objectContaining({
|
||||||
|
title: 'Login Successful!',
|
||||||
|
text: 'Welcome, React!',
|
||||||
|
icon: 'success'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows error alert on failed login', async () => {
|
||||||
|
render(<TestCase3 />);
|
||||||
|
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: /login/i });
|
||||||
|
|
||||||
|
await userEvent.type(usernameInput, 'wronguser');
|
||||||
|
await userEvent.type(passwordInput, 'wrongpassword');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.click(loginButton);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1100));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Swal.fire).toHaveBeenCalled();
|
||||||
|
|
||||||
|
const [[callArgs]] = Swal.fire.mock.calls;
|
||||||
|
|
||||||
|
expect(callArgs).toEqual(expect.objectContaining({
|
||||||
|
title: 'Login Failed!',
|
||||||
|
text: 'Username atau password salah.',
|
||||||
|
icon: 'error'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ Kode tidak sesuai dengan kunci jawaban.');
|
||||||
|
}
|
||||||
99
Reactjs1/my-app/src/components/__test__/hello.test.js
Normal file
99
Reactjs1/my-app/src/components/__test__/hello.test.js
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { diffLines } from 'diff';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import React from 'react';
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
import Hello from '../helloword/Hello';
|
||||||
|
|
||||||
|
// Fungsi untuk membaca isi file
|
||||||
|
const readFileContent = (filePath) => {
|
||||||
|
return fs.readFileSync(path.resolve(filePath), 'utf8');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fungsi untuk membandingkan isi file siswa dan file kunci jawaban
|
||||||
|
const compareFiles = (studentCode, answerKeyCode) => {
|
||||||
|
const studentLines = studentCode.split('\n');
|
||||||
|
const answerKeyLines = answerKeyCode.split('\n');
|
||||||
|
const differences = diffLines(studentCode, answerKeyCode);
|
||||||
|
let hasDifferences = false;
|
||||||
|
|
||||||
|
// Melakukan iterasi melalui perbedaan antara kode siswa dan kunci jawaban
|
||||||
|
differences.forEach((part, index) => {
|
||||||
|
if (part.added || part.removed) {
|
||||||
|
hasDifferences = true;
|
||||||
|
const lineNumber = index + 1;
|
||||||
|
const lines = part.value.split('\n');
|
||||||
|
|
||||||
|
// Log perbedaan spesifik berdasarkan baris yang diubah atau dihapus
|
||||||
|
lines.forEach((line, lineIndex) => {
|
||||||
|
const actualLine = lineNumber + lineIndex;
|
||||||
|
if (part.added) {
|
||||||
|
console.log(`Added at line ${actualLine}: ${line}`); // Baris yang ditambahkan
|
||||||
|
} else if (part.removed) {
|
||||||
|
console.log(`Removed at line ${actualLine}: ${line}`); // Baris yang dihapus
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return !hasDifferences; // Mengembalikan true jika tidak ada perbedaan
|
||||||
|
};
|
||||||
|
|
||||||
|
// Path file untuk kode siswa dan kunci jawaban
|
||||||
|
const studentFilePath = './src/components/irbaAdika/Hello.js';
|
||||||
|
const answerKeyFilePath = './src/components/kunci/Hello.js';
|
||||||
|
const studentCode = readFileContent(studentFilePath);
|
||||||
|
const answerKeyCode = readFileContent(answerKeyFilePath);
|
||||||
|
|
||||||
|
// Mengecek apakah kode siswa sesuai dengan kunci jawaban
|
||||||
|
if (compareFiles(studentCode, answerKeyCode)) {
|
||||||
|
console.log('✔ The code corresponds to the answer key. Proceed to automated testing...');
|
||||||
|
|
||||||
|
global.alert = jest.fn();
|
||||||
|
|
||||||
|
// Pengujian komponen Hello menggunakan Jest dan React Testing Library
|
||||||
|
describe('Hello component', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks(); // Membersihkan mock setelah setiap pengujian
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pengujian apakah komponen Toolbar dirender
|
||||||
|
test('renders Toolbar component', () => {
|
||||||
|
render(<Hello />);
|
||||||
|
const buttonElement = screen.getByText(/HelloWord/i);
|
||||||
|
expect(buttonElement).toBeInTheDocument();
|
||||||
|
console.log('✔ renders Toolbar component');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pengujian apakah handleHelloWorld dipanggil saat tombol diklik
|
||||||
|
test('calls handleHelloWorld function when button is clicked', () => {
|
||||||
|
render(<Hello />);
|
||||||
|
const buttonElement = screen.getByText(/HelloWord/i);
|
||||||
|
fireEvent.click(buttonElement);
|
||||||
|
|
||||||
|
expect(global.alert).toHaveBeenCalledTimes(2);
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(1, 'Hello!');
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(2, 'Goodbye!');
|
||||||
|
console.log('✔ calls handleHelloWorld function when button is clicked');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pengujian gagal jika handleHelloWorld tidak menunjukkan alert yang benar
|
||||||
|
test('fails if handleHelloWorld function does not show correct alerts', () => {
|
||||||
|
render(<Hello />);
|
||||||
|
const buttonElement = screen.getByText(/HelloWord/i);
|
||||||
|
fireEvent.click(buttonElement);
|
||||||
|
|
||||||
|
expect(global.alert).toHaveBeenCalledTimes(2);
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(1, 'Hello!');
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(2, 'Goodbye!');
|
||||||
|
console.log('✔ fails if handleHelloWorld function does not show correct alerts');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
console.log('Pengujian selesai.');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ The code does not match the answer key.'); // Notifikasi jika kode tidak sesuai dengan kunci jawaban
|
||||||
|
}
|
||||||
106
Reactjs1/my-app/src/components/backup/counterTestold.txt
Normal file
106
Reactjs1/my-app/src/components/backup/counterTestold.txt
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { Counter } from '../counter/Counter';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
describe('Counter', () => {
|
||||||
|
test('renders correctly', () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toBeInTheDocument();
|
||||||
|
const decrementButton = screen.getByRole('button', { name: 'Decrement' });
|
||||||
|
expect(decrementButton).toBeInTheDocument();
|
||||||
|
const amountInput = screen.getByRole('spinbutton');
|
||||||
|
expect(amountInput).toBeInTheDocument();
|
||||||
|
const setButton = screen.getByRole('button', { name: 'Set' });
|
||||||
|
expect(setButton).toBeInTheDocument();
|
||||||
|
const incrementButton = screen.getByRole('button', { name: 'Increment' });
|
||||||
|
expect(incrementButton).toBeInTheDocument();
|
||||||
|
const resetButton = screen.getByRole('button', { name: 'Reset' });
|
||||||
|
expect(resetButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 0', () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of -1 after clicking the decrement button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const decrementButton = screen.getByRole('button', { name: 'Decrement' });
|
||||||
|
await userEvent.click(decrementButton);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of -2 after clicking the decrement button twice', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const decrementButton = screen.getByRole('button', { name: 'Decrement' });
|
||||||
|
await userEvent.click(decrementButton);
|
||||||
|
await userEvent.click(decrementButton);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 10 after clicking the set button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const amountInput = screen.getByRole('spinbutton');
|
||||||
|
await userEvent.type(amountInput, '10');
|
||||||
|
expect(amountInput).toHaveValue(10);
|
||||||
|
const setButton = screen.getByRole('button', { name: 'Set' });
|
||||||
|
await userEvent.click(setButton);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('10');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 1 after clicking the increment button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const incrementButton = screen.getByRole('button', { name: 'Increment' });
|
||||||
|
await userEvent.click(incrementButton);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 2 after clicking the increment button twice', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const incrementButton = screen.getByRole('button', { name: 'Increment' });
|
||||||
|
await userEvent.click(incrementButton);
|
||||||
|
await userEvent.click(incrementButton);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a count of 0 after clicking the reset button', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const incrementButton = screen.getByRole('button', { name: 'Increment' });
|
||||||
|
await userEvent.click(incrementButton);
|
||||||
|
const resetButton = screen.getByRole('button', { name: 'Reset' });
|
||||||
|
await userEvent.click(resetButton);
|
||||||
|
const countElement = screen.getByRole('heading');
|
||||||
|
expect(countElement).toHaveTextContent('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('elements are focused in the right order', async () => {
|
||||||
|
render(<Counter />);
|
||||||
|
const decrementButton = screen.getByRole('button', { name: 'Decrement' });
|
||||||
|
const amountInput = screen.getByRole('spinbutton');
|
||||||
|
const setButton = screen.getByRole('button', { name: 'Set' });
|
||||||
|
const incrementButton = screen.getByRole('button', { name: 'Increment' });
|
||||||
|
const resetButton = screen.getByRole('button', { name: 'Reset' });
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(decrementButton).toHaveFocus();
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(amountInput).toHaveFocus();
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(setButton).toHaveFocus();
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(incrementButton).toHaveFocus();
|
||||||
|
|
||||||
|
await userEvent.tab();
|
||||||
|
expect(resetButton).toHaveFocus();
|
||||||
|
});
|
||||||
|
});
|
||||||
39
Reactjs1/my-app/src/components/backup/helloTestold.txt
Normal file
39
Reactjs1/my-app/src/components/backup/helloTestold.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import Hello from '../helloword/Hello';
|
||||||
|
|
||||||
|
global.alert = jest.fn();
|
||||||
|
|
||||||
|
describe('Hello component', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders Toolbar component', () => {
|
||||||
|
render(<Hello />);
|
||||||
|
const buttonElement = screen.getByText(/HelloWord/i);
|
||||||
|
expect(buttonElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calls handleHelloWorld function when button is clicked', () => {
|
||||||
|
render(<Hello />);
|
||||||
|
const buttonElement = screen.getByText(/HelloWord/i);
|
||||||
|
fireEvent.click(buttonElement);
|
||||||
|
|
||||||
|
expect(global.alert).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(1, 'Hello!');
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(2, 'Goodbye!');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fails if handleHelloWorld function does not show correct alerts', () => {
|
||||||
|
render(<Hello />);
|
||||||
|
const buttonElement = screen.getByText(/HelloWord/i);
|
||||||
|
fireEvent.click(buttonElement);
|
||||||
|
|
||||||
|
expect(global.alert).toHaveBeenCalledTimes(2);
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(1, 'Hello!');
|
||||||
|
|
||||||
|
expect(global.alert).toHaveBeenNthCalledWith(2, 'Goodbye!');
|
||||||
|
});
|
||||||
|
});
|
||||||
44
Reactjs1/my-app/src/components/backup/testCase2Testold.txt
Normal file
44
Reactjs1/my-app/src/components/backup/testCase2Testold.txt
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import user from '@testing-library/user-event';
|
||||||
|
import TestCase2 from '../form/TestCase2';
|
||||||
|
|
||||||
|
describe('TestCase2', () => {
|
||||||
|
test('renders username and password inputs correctly', () => {
|
||||||
|
render(<TestCase2 />);
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
expect(usernameInput).toBeInTheDocument();
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
expect(passwordInput).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login fails with incorrect credentials', async () => {
|
||||||
|
const originalAlert = window.alert;
|
||||||
|
window.alert = jest.fn();
|
||||||
|
|
||||||
|
render(<TestCase2 />);
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: 'Login' });
|
||||||
|
|
||||||
|
await user.type(usernameInput, 'wrongusername');
|
||||||
|
await user.type(passwordInput, 'wrongpassword');
|
||||||
|
await user.click(loginButton);
|
||||||
|
|
||||||
|
expect(screen.queryByText('Halo, wrongusername!')).not.toBeInTheDocument();
|
||||||
|
expect(window.alert).toHaveBeenCalledWith('Login gagal.');
|
||||||
|
window.alert = originalAlert;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login succeeds with correct credentials', async () => {
|
||||||
|
render(<TestCase2 />);
|
||||||
|
const usernameInput = screen.getByPlaceholderText('Username');
|
||||||
|
const passwordInput = screen.getByPlaceholderText('Password');
|
||||||
|
const loginButton = screen.getByRole('button', { name: 'Login' });
|
||||||
|
|
||||||
|
await user.type(usernameInput, 'admin');
|
||||||
|
await user.type(passwordInput, 'password');
|
||||||
|
await user.click(loginButton);
|
||||||
|
|
||||||
|
expect(screen.getByText('Halo, admin!')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
15
Reactjs1/my-app/src/components/counter/Count.js
Normal file
15
Reactjs1/my-app/src/components/counter/Count.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export const Count = () => {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const handleDecrement = () => { setCount(count + 1);};
|
||||||
|
const handleIncrement = () => { setCount(count - 1);};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{count}</h1>
|
||||||
|
<button onClick={handleIncrement}>-1</button>
|
||||||
|
<button onClick={handleDecrement}>+1</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
27
Reactjs1/my-app/src/components/counter/Counter.js
Normal file
27
Reactjs1/my-app/src/components/counter/Counter.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export const Counter = () => {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const [amount, setAmount] = useState(0);
|
||||||
|
|
||||||
|
const handleAmountChange = (e) => {
|
||||||
|
const value = parseInt(e.target.value, 10);
|
||||||
|
setAmount(isNaN(value) ? '' : value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{count}</h1>
|
||||||
|
<button onClick={() => setCount(count - 1)}>Decrement</button>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="amount"
|
||||||
|
value={amount}
|
||||||
|
onChange={handleAmountChange}
|
||||||
|
/>
|
||||||
|
<button onClick={() => setCount(amount)}>Set</button>
|
||||||
|
<button onClick={() => setCount(count + 1)}>Increment</button>
|
||||||
|
<button onClick={() => setCount(0)}>Reset</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
19
Reactjs1/my-app/src/components/form/Form.js
Normal file
19
Reactjs1/my-app/src/components/form/Form.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
export default function Form() {
|
||||||
|
const [nama, setNama] = useState('');
|
||||||
|
const [inputNama, setInputNama] = useState('');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{nama}</h1>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="inputNama"
|
||||||
|
value={inputNama}
|
||||||
|
onChange={(e) => setInputNama(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button className="set-button" onClick={() => setNama(inputNama)}>Set Nama</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
Reactjs1/my-app/src/components/form/LoginForm.js
Normal file
37
Reactjs1/my-app/src/components/form/LoginForm.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
export default function LoginForm() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [loggedInUsername, setLoggedInUsername] = useState('');
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
setLoggedInUsername(username);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{loggedInUsername ? (
|
||||||
|
<h1>Halo, {loggedInUsername}!</h1>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<button onClick={handleLogin}>Login</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
Reactjs1/my-app/src/components/form/TestCase2.js
Normal file
41
Reactjs1/my-app/src/components/form/TestCase2.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
export default function TestCase2() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
if (username === 'admin' && password === 'password') {
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
} else {
|
||||||
|
alert('Login gagal.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<h1>Halo, {username}!</h1>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<button onClick={handleLogin}>Login</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
Reactjs1/my-app/src/components/helloword/Hello.js
Normal file
30
Reactjs1/my-app/src/components/helloword/Hello.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
export default function Hello() {
|
||||||
|
const handleHelloWorld = () => {
|
||||||
|
alert('Hello!');
|
||||||
|
alert('Goodbye!');
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Toolbar
|
||||||
|
onHelloWord={handleHelloWorld}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function Toolbar({onHelloWord}){
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button onClick={onHelloWord}>HelloWord</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function Button({ onClick, children }) {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
Reactjs1/my-app/src/components/irbaAdika/Counter.js
Normal file
27
Reactjs1/my-app/src/components/irbaAdika/Counter.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export const Counter = () => {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const [amount, setAmount] = useState(0);
|
||||||
|
|
||||||
|
const handleAmountChange = (e) => {
|
||||||
|
const value = parseInt(e.target.value, 10);
|
||||||
|
setAmount(isNaN(value) ? '' : value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{count}</h1>
|
||||||
|
<button onClick={() => setCount(count - 1)}>Decrement</button>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="amount"
|
||||||
|
value={amount}
|
||||||
|
onChange={handleAmountChange}
|
||||||
|
/>
|
||||||
|
<button onClick={() => setCount(amount)}>Set</button>
|
||||||
|
<button onClick={() => setCount(count + 1)}>Increment</button>
|
||||||
|
<button onClick={() => setCount(0)}>Reset</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
41
Reactjs1/my-app/src/components/irbaAdika/Form.js
Normal file
41
Reactjs1/my-app/src/components/irbaAdika/Form.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
export default function TestCase2() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
if (username === 'admin' && password === 'password') {
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
} else {
|
||||||
|
alert('Login gagal.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<h1>Halo, {username}!</h1>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<button onClick={handleLogin}>Login</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
82
Reactjs1/my-app/src/components/irbaAdika/FormStyle.js
Normal file
82
Reactjs1/my-app/src/components/irbaAdika/FormStyle.js
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Login.css';
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (username === 'React' && password === 'password') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Successful!',
|
||||||
|
text: `Welcome, ${username}!`,
|
||||||
|
icon: 'success',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Failed!',
|
||||||
|
text: 'Username atau password salah.',
|
||||||
|
icon: 'error',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="login-page">
|
||||||
|
<div className="login-container">
|
||||||
|
<div className="container">
|
||||||
|
<h1>Login</h1>
|
||||||
|
<div className="login-content">
|
||||||
|
<div className="login-image">
|
||||||
|
<img className="login-images" src='/images/React.gif' alt="Login" />
|
||||||
|
</div>
|
||||||
|
<div className="login-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="checkbox-container">
|
||||||
|
<label className="checkbox-label">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={showPassword}
|
||||||
|
onChange={() => setShowPassword(!showPassword)}
|
||||||
|
/> Show Password
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button onClick={handleLogin} disabled={isLoading}>
|
||||||
|
{isLoading ? 'Logging in...' : 'Login'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="additional-container">
|
||||||
|
<h2>Welcome to Reactjs!</h2>
|
||||||
|
<p>Belajar User Interactive Interactive Dari Reactjs</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
Reactjs1/my-app/src/components/irbaAdika/Hello.js
Normal file
30
Reactjs1/my-app/src/components/irbaAdika/Hello.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
export default function Hello() {
|
||||||
|
const handleHelloWorld = () => {
|
||||||
|
alert('Hello!');
|
||||||
|
alert('Goodbye!');
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Toolbar
|
||||||
|
onHelloWord={handleHelloWorld}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function Toolbar({onHelloWord}){
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button onClick={onHelloWord}>HelloWord</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function Button({ onClick, children }) {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
45
Reactjs1/my-app/src/components/irbaAdika/Navbar.js
Normal file
45
Reactjs1/my-app/src/components/irbaAdika/Navbar.js
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Navbar.css';
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Logout',
|
||||||
|
text: 'Apakah Anda yakin ingin logout?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya',
|
||||||
|
cancelButtonText: 'Tidak',
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Logged Out',
|
||||||
|
text: 'You have been successfully logged out.',
|
||||||
|
icon: 'info',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
navigate('/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="navbar">
|
||||||
|
<ul className="navbar-list">
|
||||||
|
<li className="navbar-item"><a href="/home" className="navbar-link">Home</a></li>
|
||||||
|
<li className="navbar-item"><a href="/todolist" className="navbar-link">Todolist</a></li>
|
||||||
|
<li className="navbar-item"><a href="/calculator" className="navbar-link">Calculator</a></li>
|
||||||
|
<li className="navbar-item"><button onClick={handleLogout} className="logout-button">Logout</button></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
||||||
27
Reactjs1/my-app/src/components/kunci/Counter.js
Normal file
27
Reactjs1/my-app/src/components/kunci/Counter.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export const Counter = () => {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const [amount, setAmount] = useState(0);
|
||||||
|
|
||||||
|
const handleAmountChange = (e) => {
|
||||||
|
const value = parseInt(e.target.value, 10);
|
||||||
|
setAmount(isNaN(value) ? '' : value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{count}</h1>
|
||||||
|
<button onClick={() => setCount(count - 1)}>Decrement</button>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="amount"
|
||||||
|
value={amount}
|
||||||
|
onChange={handleAmountChange}
|
||||||
|
/>
|
||||||
|
<button onClick={() => setCount(amount)}>Set</button>
|
||||||
|
<button onClick={() => setCount(count + 1)}>Increment</button>
|
||||||
|
<button onClick={() => setCount(0)}>Reset</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
41
Reactjs1/my-app/src/components/kunci/Form.js
Normal file
41
Reactjs1/my-app/src/components/kunci/Form.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
export default function TestCase2() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
if (username === 'admin' && password === 'password') {
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
} else {
|
||||||
|
alert('Login gagal.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<h1>Halo, {username}!</h1>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<button onClick={handleLogin}>Login</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
82
Reactjs1/my-app/src/components/kunci/FormStyle.js
Normal file
82
Reactjs1/my-app/src/components/kunci/FormStyle.js
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Login.css';
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (username === 'React' && password === 'password') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Successful!',
|
||||||
|
text: `Welcome, ${username}!`,
|
||||||
|
icon: 'success',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Failed!',
|
||||||
|
text: 'Username atau password salah.',
|
||||||
|
icon: 'error',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="login-page">
|
||||||
|
<div className="login-container">
|
||||||
|
<div className="container">
|
||||||
|
<h1>Login</h1>
|
||||||
|
<div className="login-content">
|
||||||
|
<div className="login-image">
|
||||||
|
<img className="login-images" src='/images/React.gif' alt="Login" />
|
||||||
|
</div>
|
||||||
|
<div className="login-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="checkbox-container">
|
||||||
|
<label className="checkbox-label">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={showPassword}
|
||||||
|
onChange={() => setShowPassword(!showPassword)}
|
||||||
|
/> Show Password
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button onClick={handleLogin} disabled={isLoading}>
|
||||||
|
{isLoading ? 'Logging in...' : 'Login'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="additional-container">
|
||||||
|
<h2>Welcome to Reactjs!</h2>
|
||||||
|
<p>Belajar User Interactive Interactive Dari Reactjs</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
Reactjs1/my-app/src/components/kunci/Hello.js
Normal file
30
Reactjs1/my-app/src/components/kunci/Hello.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
export default function Hello() {
|
||||||
|
const handleHelloWorld = () => {
|
||||||
|
alert('Hello!');
|
||||||
|
alert('Goodbye!');
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Toolbar
|
||||||
|
onHelloWord={handleHelloWorld}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function Toolbar({onHelloWord}){
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button onClick={onHelloWord}>HelloWord</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function Button({ onClick, children }) {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
45
Reactjs1/my-app/src/components/kunci/Navbar.js
Normal file
45
Reactjs1/my-app/src/components/kunci/Navbar.js
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Navbar.css';
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Logout',
|
||||||
|
text: 'Apakah Anda yakin ingin logout?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya',
|
||||||
|
cancelButtonText: 'Tidak',
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Logged Out',
|
||||||
|
text: 'You have been successfully logged out.',
|
||||||
|
icon: 'info',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
navigate('/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="navbar">
|
||||||
|
<ul className="navbar-list">
|
||||||
|
<li className="navbar-item"><a href="/home" className="navbar-link">Home</a></li>
|
||||||
|
<li className="navbar-item"><a href="/todolist" className="navbar-link">Todolist</a></li>
|
||||||
|
<li className="navbar-item"><a href="/calculator" className="navbar-link">Calculator</a></li>
|
||||||
|
<li className="navbar-item"><button onClick={handleLogout} className="logout-button">Logout</button></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
||||||
30
Reactjs1/my-app/src/components/websitereact/Calculator.css
Normal file
30
Reactjs1/my-app/src/components/websitereact/Calculator.css
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
.calculator-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad button {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad button:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#result {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
63
Reactjs1/my-app/src/components/websitereact/Calculator.js
Normal file
63
Reactjs1/my-app/src/components/websitereact/Calculator.js
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import './Calculator.css'
|
||||||
|
import Navbar from './Navbar';
|
||||||
|
import Footer from './Footer';
|
||||||
|
|
||||||
|
const Calculator = () => {
|
||||||
|
const [result, setResult] = useState('');
|
||||||
|
|
||||||
|
const handleClick = (e) => {
|
||||||
|
setResult(result.concat(e.target.name));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
setResult('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const backspace = () => {
|
||||||
|
setResult(result.slice(0, -1));
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculate = () => {
|
||||||
|
try {
|
||||||
|
setResult(eval(result).toString());
|
||||||
|
} catch (err) {
|
||||||
|
setResult('Error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar/>
|
||||||
|
<h1>Calculator</h1>
|
||||||
|
<div className="calculator-container">
|
||||||
|
<form>
|
||||||
|
<input type="text" value={result} readOnly />
|
||||||
|
</form>
|
||||||
|
<div className="keypad">
|
||||||
|
<button onClick={clear} id="clear">Clear</button>
|
||||||
|
<button onClick={backspace} id="backspace">C</button>
|
||||||
|
<button name="/" onClick={handleClick}>÷</button>
|
||||||
|
<button name="7" onClick={handleClick}>7</button>
|
||||||
|
<button name="8" onClick={handleClick}>8</button>
|
||||||
|
<button name="9" onClick={handleClick}>9</button>
|
||||||
|
<button name="*" onClick={handleClick}>×</button>
|
||||||
|
<button name="4" onClick={handleClick}>4</button>
|
||||||
|
<button name="5" onClick={handleClick}>5</button>
|
||||||
|
<button name="6" onClick={handleClick}>6</button>
|
||||||
|
<button name="-" onClick={handleClick}>–</button>
|
||||||
|
<button name="1" onClick={handleClick}>1</button>
|
||||||
|
<button name="2" onClick={handleClick}>2</button>
|
||||||
|
<button name="3" onClick={handleClick}>3</button>
|
||||||
|
<button name="+" onClick={handleClick}>+</button>
|
||||||
|
<button name="0" onClick={handleClick}>0</button>
|
||||||
|
<button name="." onClick={handleClick}>.</button>
|
||||||
|
<button onClick={calculate} id="result">=</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Calculator;
|
||||||
41
Reactjs1/my-app/src/components/websitereact/Footer.css
Normal file
41
Reactjs1/my-app/src/components/websitereact/Footer.css
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
.footer {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
grid-gap: 1rem;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-heading {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul li a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-bottom {
|
||||||
|
margin-top: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
31
Reactjs1/my-app/src/components/websitereact/Footer.js
Normal file
31
Reactjs1/my-app/src/components/websitereact/Footer.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import './Footer.css'
|
||||||
|
|
||||||
|
const Footer = () => {
|
||||||
|
return (
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="footer-content">
|
||||||
|
<div className="footer-section about">
|
||||||
|
<h3 className="footer-heading">About Us</h3>
|
||||||
|
<p>We are a company dedicated to providing the best services and solutions to our customers. Our mission is to deliver high-quality products and exceptional customer support.</p>
|
||||||
|
</div>
|
||||||
|
<div className="footer-section links">
|
||||||
|
<h3 className="footer-heading">Quick Links</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/home">Home</a></li>
|
||||||
|
<li><a href="/todolist">TodoList</a></li>
|
||||||
|
<li><a href="/calculator">Calculator</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="footer-section social">
|
||||||
|
<h3 className="footer-heading">Follow Us</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="footer-bottom">
|
||||||
|
<p>© 2024 Belajar Reactjs. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
66
Reactjs1/my-app/src/components/websitereact/Home.css
Normal file
66
Reactjs1/my-app/src/components/websitereact/Home.css
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
.home-container {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
color: #333 ;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-header {
|
||||||
|
background-color: #82caff;
|
||||||
|
color: white;
|
||||||
|
padding: 50px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-header p {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
margin: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 300px;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card h2 {
|
||||||
|
color: #82caff;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card p {
|
||||||
|
color: #666;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
31
Reactjs1/my-app/src/components/websitereact/Home.js
Normal file
31
Reactjs1/my-app/src/components/websitereact/Home.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Footer from './Footer';
|
||||||
|
import Navbar from './Navbar';
|
||||||
|
import './Home.css';
|
||||||
|
|
||||||
|
const Home = ({ username}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar />
|
||||||
|
<div className="home-container">
|
||||||
|
<header className="home-header">
|
||||||
|
<h1>Selamat Datang, {username}</h1>
|
||||||
|
<p>Mari Belajar UI Interactive Dengan Reactjs</p>
|
||||||
|
</header>
|
||||||
|
<section className="features">
|
||||||
|
<div className="feature-card">
|
||||||
|
<h2>Membuat TodoList</h2>
|
||||||
|
<a href='/todolist' className="feature-link">Mencatat Kegiatan Yang Kamu Lakukan.</a>
|
||||||
|
</div>
|
||||||
|
<div className="feature-card">
|
||||||
|
<h2>Membuat Calculator</h2>
|
||||||
|
<a href='/calculator' className="feature-link">Melakukan Perhitungan Angka.</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
||||||
92
Reactjs1/my-app/src/components/websitereact/Login.css
Normal file
92
Reactjs1/my-app/src/components/websitereact/Login.css
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
.login-page {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-image {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-images {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="password"],
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #82caff;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #60b5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #ff0000;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.additional-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
}
|
||||||
85
Reactjs1/my-app/src/components/websitereact/Login.js
Normal file
85
Reactjs1/my-app/src/components/websitereact/Login.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Login.css';
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (username === 'React' && password === 'password') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Successful!',
|
||||||
|
text: `Welcome, ${username}!`,
|
||||||
|
icon: 'success',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
setIsLoading(false);
|
||||||
|
navigate('/home');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Failed!',
|
||||||
|
text: 'Username atau password salah.',
|
||||||
|
icon: 'error',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="login-page">
|
||||||
|
<div className="login-container">
|
||||||
|
<div className="container">
|
||||||
|
<h1>Login</h1>
|
||||||
|
<div className="login-content">
|
||||||
|
<div className="login-image">
|
||||||
|
<img className="login-images" src='/images/React.gif' alt="Login" />
|
||||||
|
</div>
|
||||||
|
<div className="login-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="checkbox-container">
|
||||||
|
<label className="checkbox-label">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={showPassword}
|
||||||
|
onChange={() => setShowPassword(!showPassword)}
|
||||||
|
/> Show Password
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button onClick={handleLogin} disabled={isLoading}>
|
||||||
|
{isLoading ? 'Logging in...' : 'Login'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="additional-container">
|
||||||
|
<h2>Welcome to Reactjs!</h2>
|
||||||
|
<p>Belajar User Interactive Interactive Dari Reactjs</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
44
Reactjs1/my-app/src/components/websitereact/Navbar.css
Normal file
44
Reactjs1/my-app/src/components/websitereact/Navbar.css
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background-color: #333;
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-list {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-item {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link:hover {
|
||||||
|
color: #82caff;
|
||||||
|
}
|
||||||
|
.logout-button {
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-button:hover {
|
||||||
|
background-color: darkred;
|
||||||
|
}
|
||||||
45
Reactjs1/my-app/src/components/websitereact/Navbar.js
Normal file
45
Reactjs1/my-app/src/components/websitereact/Navbar.js
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Navbar.css';
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Logout',
|
||||||
|
text: 'Apakah Anda yakin ingin logout?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya',
|
||||||
|
cancelButtonText: 'Tidak',
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Logged Out',
|
||||||
|
text: 'You have been successfully logged out.',
|
||||||
|
icon: 'info',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
navigate('/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="navbar">
|
||||||
|
<ul className="navbar-list">
|
||||||
|
<li className="navbar-item"><a href="/home" className="navbar-link">Home</a></li>
|
||||||
|
<li className="navbar-item"><a href="/todolist" className="navbar-link">Todolist</a></li>
|
||||||
|
<li className="navbar-item"><a href="/calculator" className="navbar-link">Calculator</a></li>
|
||||||
|
<li className="navbar-item"><button onClick={handleLogout} className="logout-button">Logout</button></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
||||||
82
Reactjs1/my-app/src/components/websitereact/TestCase3.js
Normal file
82
Reactjs1/my-app/src/components/websitereact/TestCase3.js
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import './Login.css';
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const handleLogin = () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (username === 'React' && password === 'password') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Successful!',
|
||||||
|
text: `Welcome, ${username}!`,
|
||||||
|
icon: 'success',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true,
|
||||||
|
willClose: () => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Failed!',
|
||||||
|
text: 'Username atau password salah.',
|
||||||
|
icon: 'error',
|
||||||
|
timer: 1000,
|
||||||
|
timerProgressBar: true
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="login-page">
|
||||||
|
<div className="login-container">
|
||||||
|
<div className="container">
|
||||||
|
<h1>Login</h1>
|
||||||
|
<div className="login-content">
|
||||||
|
<div className="login-image">
|
||||||
|
<img className="login-images" src='/images/React.gif' alt="Login" />
|
||||||
|
</div>
|
||||||
|
<div className="login-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="checkbox-container">
|
||||||
|
<label className="checkbox-label">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={showPassword}
|
||||||
|
onChange={() => setShowPassword(!showPassword)}
|
||||||
|
/> Show Password
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button onClick={handleLogin} disabled={isLoading}>
|
||||||
|
{isLoading ? 'Logging in...' : 'Login'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="additional-container">
|
||||||
|
<h2>Welcome to Reactjs!</h2>
|
||||||
|
<p>Belajar User Interactive Interactive Dari Reactjs</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
53
Reactjs1/my-app/src/components/websitereact/Todolist.css
Normal file
53
Reactjs1/my-app/src/components/websitereact/Todolist.css
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
.todo-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 6rem;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container input[type="text"] {
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-add {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #038af9;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button {
|
||||||
|
margin-left: auto;
|
||||||
|
background-color: #dc3545;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
48
Reactjs1/my-app/src/components/websitereact/Todolist.js
Normal file
48
Reactjs1/my-app/src/components/websitereact/Todolist.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Navbar from './Navbar';
|
||||||
|
import Footer from './Footer';
|
||||||
|
import './Todolist.css'
|
||||||
|
|
||||||
|
const TodoList = () => {
|
||||||
|
const [todos, setTodos] = useState([]);
|
||||||
|
const [newTodo, setNewTodo] = useState('');
|
||||||
|
|
||||||
|
const addTodo = () => {
|
||||||
|
if (newTodo.trim()) {
|
||||||
|
setTodos([...todos, newTodo.trim()]);
|
||||||
|
setNewTodo('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteTodo = (indexToDelete) => {
|
||||||
|
const updatedTodos = todos.filter((_, index) => index !== indexToDelete);
|
||||||
|
setTodos(updatedTodos);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar />
|
||||||
|
<div className="todo-container">
|
||||||
|
<h2>Todo List</h2>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={newTodo}
|
||||||
|
onChange={(e) => setNewTodo(e.target.value)}
|
||||||
|
placeholder="Add a new task"
|
||||||
|
/>
|
||||||
|
<button className='button-add'onClick={addTodo}>Add</button>
|
||||||
|
<ul>
|
||||||
|
{todos.map((todo, index) => (
|
||||||
|
<li key={index}>
|
||||||
|
{todo}
|
||||||
|
<button className="delete-button" onClick={() => deleteTodo(index)}>Delete</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TodoList;
|
||||||
343
Reactjs1/my-app/src/components/websitereact/Website.css
Normal file
343
Reactjs1/my-app/src/components/websitereact/Website.css
Normal file
|
|
@ -0,0 +1,343 @@
|
||||||
|
/* .container {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.checkbox-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="password"],
|
||||||
|
button {
|
||||||
|
width: 94%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: block;
|
||||||
|
background-color: #82caff;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #82caff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #ff0000;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background-color: #333;
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-list {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-item {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link:hover {
|
||||||
|
color: #82caff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-container {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-header {
|
||||||
|
background-color: #82caff;
|
||||||
|
color: white;
|
||||||
|
padding: 50px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-header p {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
margin: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 300px;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card h2 {
|
||||||
|
color: #82caff;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card p {
|
||||||
|
color: #666;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.calculator-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad button {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad button:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#result {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.todo-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container input {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container button:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container li {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container input {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container button:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-container li {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: auto;
|
||||||
|
text-align: center;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-heading {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section p, .footer-section ul {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul li {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul li a {
|
||||||
|
color: #ffffff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul li a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-icon {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-icon:hover {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-bottom {
|
||||||
|
margin-top: 20px;
|
||||||
|
border-top: 1px solid #444;
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.footer-content {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
} */
|
||||||
13
Reactjs1/my-app/src/index.css
Normal file
13
Reactjs1/my-app/src/index.css
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
17
Reactjs1/my-app/src/index.js
Normal file
17
Reactjs1/my-app/src/index.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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();
|
||||||
1
Reactjs1/my-app/src/logo.svg
Normal file
1
Reactjs1/my-app/src/logo.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
13
Reactjs1/my-app/src/reportWebVitals.js
Normal file
13
Reactjs1/my-app/src/reportWebVitals.js
Normal 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
Reactjs1/my-app/src/setupTests.js
Normal file
5
Reactjs1/my-app/src/setupTests.js
Normal 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';
|
||||||
BIN
iCLOP_React.js_Komang.mp4
Normal file
BIN
iCLOP_React.js_Komang.mp4
Normal file
Binary file not shown.
18
iClOP-V2/.editorconfig
Normal file
18
iClOP-V2/.editorconfig
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[docker-compose.yml]
|
||||||
|
indent_size = 4
|
||||||
65
iClOP-V2/.env.example
Normal file
65
iClOP-V2/.env.example
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
APP_NAME=iCLOP
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=laravel
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
NODEJS_DB_HOST=127.0.0.1
|
||||||
|
NODEJS_DB_PORT=3306
|
||||||
|
NODEJS_DB_DATABASE=iclop_nodejs
|
||||||
|
NODEJS_DB_USERNAME=root
|
||||||
|
NODEJS_DB_PASSWORD=
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=mailpit
|
||||||
|
MAIL_PORT=1025
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_HOST=
|
||||||
|
PUSHER_PORT=443
|
||||||
|
PUSHER_SCHEME=https
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||||
|
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||||
|
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||||
|
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
11
iClOP-V2/.gitattributes
vendored
Normal file
11
iClOP-V2/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
*.blade.php diff=html
|
||||||
|
*.css diff=css
|
||||||
|
*.html diff=html
|
||||||
|
*.md diff=markdown
|
||||||
|
*.php diff=php
|
||||||
|
|
||||||
|
/.github export-ignore
|
||||||
|
CHANGELOG.md export-ignore
|
||||||
|
.styleci.yml export-ignore
|
||||||
19
iClOP-V2/.gitignore
vendored
Normal file
19
iClOP-V2/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/.phpunit.cache
|
||||||
|
/node_modules
|
||||||
|
/public/build
|
||||||
|
/public/hot
|
||||||
|
/public/storage
|
||||||
|
/storage/*.key
|
||||||
|
/vendor
|
||||||
|
.env
|
||||||
|
.env.backup
|
||||||
|
.env.production
|
||||||
|
.phpunit.result.cache
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
auth.json
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
/.fleet
|
||||||
|
/.idea
|
||||||
|
/.vscode
|
||||||
6
iClOP-V2/.htaccess
Normal file
6
iClOP-V2/.htaccess
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteCond %{REQUEST_URI} !^public
|
||||||
|
RewriteRule ^(.*)$ public/$1 [L]
|
||||||
|
|
||||||
|
</IfModule>
|
||||||
66
iClOP-V2/README.md
Normal file
66
iClOP-V2/README.md
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||||
|
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||||
|
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||||
|
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## About Laravel
|
||||||
|
|
||||||
|
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||||
|
|
||||||
|
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||||
|
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||||
|
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||||
|
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||||
|
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||||
|
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||||
|
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||||
|
|
||||||
|
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||||
|
|
||||||
|
## Learning Laravel
|
||||||
|
|
||||||
|
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||||
|
|
||||||
|
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||||
|
|
||||||
|
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 2000 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||||
|
|
||||||
|
## Laravel Sponsors
|
||||||
|
|
||||||
|
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||||
|
|
||||||
|
### Premium Partners
|
||||||
|
|
||||||
|
- **[Vehikl](https://vehikl.com/)**
|
||||||
|
- **[Tighten Co.](https://tighten.co)**
|
||||||
|
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||||
|
- **[64 Robots](https://64robots.com)**
|
||||||
|
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||||
|
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||||
|
- **[Many](https://www.many.co.uk)**
|
||||||
|
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||||
|
- **[DevSquad](https://devsquad.com)**
|
||||||
|
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||||
|
- **[OP.GG](https://op.gg)**
|
||||||
|
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
|
||||||
|
- **[Lendio](https://lendio.com)**
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||||
|
|
||||||
|
## Security Vulnerabilities
|
||||||
|
|
||||||
|
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||||
27
iClOP-V2/app/Console/Kernel.php
Normal file
27
iClOP-V2/app/Console/Kernel.php
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console;
|
||||||
|
|
||||||
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
|
class Kernel extends ConsoleKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the application's command schedule.
|
||||||
|
*/
|
||||||
|
protected function schedule(Schedule $schedule): void
|
||||||
|
{
|
||||||
|
// $schedule->command('inspire')->hourly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the commands for the application.
|
||||||
|
*/
|
||||||
|
protected function commands(): void
|
||||||
|
{
|
||||||
|
$this->load(__DIR__.'/Commands');
|
||||||
|
|
||||||
|
require base_path('routes/console.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
30
iClOP-V2/app/Exceptions/Handler.php
Normal file
30
iClOP-V2/app/Exceptions/Handler.php
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Handler extends ExceptionHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $dontFlash = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the exception handling callbacks for the application.
|
||||||
|
*/
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
$this->reportable(function (Throwable $e) {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
844
iClOP-V2/app/Http/Controllers/Android/AndroidController.php
Normal file
844
iClOP-V2/app/Http/Controllers/Android/AndroidController.php
Normal file
|
|
@ -0,0 +1,844 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Android;
|
||||||
|
|
||||||
|
use App\Models\Android\Topic;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Android\Task;
|
||||||
|
use App\Models\Android\Task_waiting;
|
||||||
|
use App\Models\Android\Enrollment;
|
||||||
|
use App\Models\Android\SubmitstestCase;
|
||||||
|
use App\Models\Android\SubmitsFinalSubmission;
|
||||||
|
use App\Models\Android\Submits;
|
||||||
|
use App\Models\Android\Testcase;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use DB;
|
||||||
|
|
||||||
|
class AndroidController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
- LECTURER
|
||||||
|
*/
|
||||||
|
public function lecturer_material(){
|
||||||
|
|
||||||
|
$dt_keseluruhan = array();
|
||||||
|
$topic = Topic::all();
|
||||||
|
|
||||||
|
$notifikasi = $this->notify_validator();
|
||||||
|
|
||||||
|
$total_mhs = 0;
|
||||||
|
|
||||||
|
if ( $topic->count() > 0 ) {
|
||||||
|
|
||||||
|
foreach ( $topic AS $isi ) {
|
||||||
|
|
||||||
|
// count data task
|
||||||
|
$isi->total = Task::where('android_topic_id', $isi->id)->count();
|
||||||
|
|
||||||
|
$total_mhs_bytopik = Enrollment::where('android_topic_id', $isi->id)->whereNotIn('status', ['cancel'])->get()->count();
|
||||||
|
$isi->enroll = $total_mhs_bytopik;
|
||||||
|
|
||||||
|
array_push( $dt_keseluruhan, $isi );
|
||||||
|
|
||||||
|
|
||||||
|
$total_mhs = $total_mhs + $total_mhs_bytopik;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return view('android.teacher.material', compact('dt_keseluruhan', 'total_mhs', 'notifikasi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function lecturer_overview( $id ){
|
||||||
|
|
||||||
|
$topic = Topic::findOrFail( $id );
|
||||||
|
$task = Task::where('android_topic_id', $id)->get();
|
||||||
|
|
||||||
|
|
||||||
|
$dt_enrollment = array();
|
||||||
|
$enrollment = Enrollment::where('android_topic_id', $id)->get();
|
||||||
|
|
||||||
|
foreach ( $enrollment As $isi ) {
|
||||||
|
|
||||||
|
// informasi validation
|
||||||
|
$total_request = 0;
|
||||||
|
$total_validate = 0;
|
||||||
|
$user_id = $isi->user->id;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ambil informasi data testcase
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'android_topic_id' => $id
|
||||||
|
);
|
||||||
|
$submit = Submits::where($where)->get();
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - -
|
||||||
|
$NA = 0;
|
||||||
|
$total_submit = $submit->count();
|
||||||
|
|
||||||
|
// - - - - - - - -
|
||||||
|
|
||||||
|
if ( $submit->count() > 0 ) {
|
||||||
|
|
||||||
|
|
||||||
|
$total_NA = 0;
|
||||||
|
foreach ( $submit AS $isi_s ) {
|
||||||
|
|
||||||
|
// ambil data testcase
|
||||||
|
$SubmitstestCase = SubmitstestCase::where("android_submit_id", $isi_s->id)->get();
|
||||||
|
$isi->testcase = $SubmitstestCase;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// auto
|
||||||
|
$where_passed = [
|
||||||
|
|
||||||
|
'android_submit_id' => $isi_s->id,
|
||||||
|
'status_validate' => "passed"
|
||||||
|
];
|
||||||
|
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||||
|
|
||||||
|
// ambil informasi data task untuk memanggil bobot
|
||||||
|
$info_testcase = Testcase::where("task_id", $isi_s->android_task_id)->get();
|
||||||
|
|
||||||
|
$bobot = 0;
|
||||||
|
foreach ( $info_testcase AS $isiit ) {
|
||||||
|
|
||||||
|
$bobot += $isiit->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skor = 0;
|
||||||
|
foreach ( $test_passed AS $isitp ) {
|
||||||
|
|
||||||
|
$skor += $isitp->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nilai = 0;
|
||||||
|
if ( $skor > 0 && $bobot > 0 ) {
|
||||||
|
|
||||||
|
$nilai = $skor / $bobot * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
$total_NA += $nilai;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// autograding
|
||||||
|
// $passed = 0;
|
||||||
|
// foreach ( $SubmitstestCase AS $det ) {
|
||||||
|
|
||||||
|
// if ( $det->status_validate == "passed" ) {
|
||||||
|
|
||||||
|
// $passed++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $total_NA += ($passed / $SubmitstestCase->count() * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo $total_submit;
|
||||||
|
$NA = $total_NA / $total_submit;
|
||||||
|
|
||||||
|
|
||||||
|
$total_request = $submit->count();
|
||||||
|
foreach ( $submit AS $det ) {
|
||||||
|
|
||||||
|
if ( $det->validator == "validated" ) $total_validate++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$progress = 0;
|
||||||
|
$total_task = Task::where("android_topic_id", $isi->android_topic_id)->count();
|
||||||
|
$status = $isi->status;
|
||||||
|
|
||||||
|
// check progress
|
||||||
|
if ( $status != "cancel") {
|
||||||
|
|
||||||
|
$task_waiting = Task_waiting::where( $where );
|
||||||
|
$total_task_waiting = $task_waiting->count();
|
||||||
|
|
||||||
|
if ( $total_task_waiting > 0 ) {
|
||||||
|
|
||||||
|
$info = Task::find( $task_waiting->first()->android_task_id );
|
||||||
|
$progress = $total_task_waiting / $total_task * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$isi->total_request = $total_request;
|
||||||
|
$isi->total_validate = $total_validate;
|
||||||
|
$isi->NA = $NA;
|
||||||
|
$isi->progress = $progress;
|
||||||
|
|
||||||
|
|
||||||
|
array_push( $dt_enrollment, $isi );
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'task' => $task,
|
||||||
|
'topic' => $topic,
|
||||||
|
'enrollment' => $dt_enrollment
|
||||||
|
);
|
||||||
|
// print_r( $Enrollment->user->name );
|
||||||
|
|
||||||
|
return view('android.teacher.overview', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function lecturer_waiting() {
|
||||||
|
|
||||||
|
// ambil data
|
||||||
|
$dt_keseluruhan = array();
|
||||||
|
$topic = Topic::all();
|
||||||
|
|
||||||
|
$notifikasi = $this->notify_validator();
|
||||||
|
|
||||||
|
$total_mhs = 0;
|
||||||
|
|
||||||
|
if ( $topic->count() > 0 ) {
|
||||||
|
|
||||||
|
foreach ( $topic AS $isi ) {
|
||||||
|
|
||||||
|
// count data task
|
||||||
|
$isi->total = Task::where('android_topic_id', $isi->id)->count();
|
||||||
|
|
||||||
|
$total_mhs_bytopik = Enrollment::where('android_topic_id', $isi->id)->whereNotIn('status', ['cancel'])->get()->count();
|
||||||
|
$isi->enroll = $total_mhs_bytopik;
|
||||||
|
|
||||||
|
array_push( $dt_keseluruhan, $isi );
|
||||||
|
|
||||||
|
|
||||||
|
$total_mhs = $total_mhs + $total_mhs_bytopik;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cek */
|
||||||
|
$submits = Submits::where('validator', 'process')->get();
|
||||||
|
$dt_need_validator = array();
|
||||||
|
|
||||||
|
if ( $submits->count() > 0 ) {
|
||||||
|
|
||||||
|
// group by
|
||||||
|
$dt_submit_topic = array();
|
||||||
|
foreach ( $submits AS $index => $isi ) {
|
||||||
|
|
||||||
|
if ( $index > 0 ) {
|
||||||
|
|
||||||
|
$find = in_array($isi->android_topic_id, array_column($dt_submit_topic, 'android_topic_id'));
|
||||||
|
if ( $find == false ) {
|
||||||
|
|
||||||
|
array_push( $dt_submit_topic, [
|
||||||
|
'android_topic_id' => $isi->android_topic_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
array_push( $dt_submit_topic, [
|
||||||
|
'android_topic_id' => $isi->android_topic_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// akumulasi mahasiswa
|
||||||
|
foreach ( $dt_submit_topic AS $isi ) {
|
||||||
|
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'android_topic_id' => $isi,
|
||||||
|
'validator' => "process"
|
||||||
|
);
|
||||||
|
$all_mhs = Submits::where($where)->get();
|
||||||
|
$jumlah = $all_mhs->count();
|
||||||
|
|
||||||
|
$dt_all_mahasiswa_by_topic = array();
|
||||||
|
|
||||||
|
|
||||||
|
$title = "";
|
||||||
|
if ( $jumlah > 0 ) {
|
||||||
|
|
||||||
|
foreach ( $all_mhs AS $mhs ) {
|
||||||
|
|
||||||
|
$title = $mhs->topic->title;
|
||||||
|
array_push( $dt_all_mahasiswa_by_topic, $mhs );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// push to validator
|
||||||
|
array_push( $dt_need_validator, [
|
||||||
|
|
||||||
|
'android_topic_id' => $isi,
|
||||||
|
'title' => $title,
|
||||||
|
'jumlah' => $jumlah,
|
||||||
|
'all_mhs' => $dt_all_mahasiswa_by_topic
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('android.teacher.validator_waiting', compact('notifikasi', 'total_mhs', 'dt_need_validator'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// detail task
|
||||||
|
public function lecturer_waiting_preview( $id_submit ) {
|
||||||
|
|
||||||
|
$submit = Submits::findOrFail( $id_submit );
|
||||||
|
// $testcase = SubmitstestCase::where("android_submit_id", $id_submit)->->get();
|
||||||
|
$testcase = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where("android_submit_id", $id_submit)->select("android_submits_testcase.*", "case", "score")->get();
|
||||||
|
|
||||||
|
// echo json_encode($testcase);
|
||||||
|
return view('android.teacher.validator_detail', compact('submit', 'testcase'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// do validate
|
||||||
|
public function lecturer_do_validate( $id_testcase, $android_submit_id ) {
|
||||||
|
|
||||||
|
|
||||||
|
// update all status waiting to failed (default)
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'android_submit_id' => $android_submit_id,
|
||||||
|
'status_validate' => "waiting"
|
||||||
|
);
|
||||||
|
SubmitstestCase::where($where)->update(['status_validate' => "failed"]);
|
||||||
|
|
||||||
|
$submittestcase = SubmitstestCase::findOrFail( $id_testcase );
|
||||||
|
$submit = Submits::findOrFail( $android_submit_id );
|
||||||
|
|
||||||
|
$submit->validator = "validated";
|
||||||
|
$submit->save();
|
||||||
|
|
||||||
|
|
||||||
|
if ( $submittestcase->status_validate == "failed" ) {
|
||||||
|
$submittestcase->status_validate = "passed";
|
||||||
|
} else if ( $submittestcase->status_validate == "passed" ) {
|
||||||
|
$submittestcase->status_validate = "failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
$submittestcase->save();
|
||||||
|
|
||||||
|
|
||||||
|
// echo $;
|
||||||
|
// return "oke";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function lecturer_load_point_testcase( $android_submit_id ) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$where = [
|
||||||
|
|
||||||
|
'android_submit_id' => $android_submit_id,
|
||||||
|
'status_validate' => "passed"
|
||||||
|
];
|
||||||
|
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where)->get();
|
||||||
|
|
||||||
|
|
||||||
|
// ambil data submit berdasarkan id
|
||||||
|
$submit = Submits::findOrFail( $android_submit_id );
|
||||||
|
$task = Task::findOrFail( $submit->android_task_id );
|
||||||
|
|
||||||
|
$info_testcase = Testcase::where("task_id", $task->id)->get();
|
||||||
|
|
||||||
|
$bobot = 0;
|
||||||
|
foreach ( $info_testcase AS $isi ) {
|
||||||
|
|
||||||
|
$bobot += $isi->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$skor = 0;
|
||||||
|
foreach ( $test_passed AS $isi ) {
|
||||||
|
|
||||||
|
$skor += $isi->score;
|
||||||
|
}
|
||||||
|
// hitung bobot per testcase
|
||||||
|
$nilai = 0;
|
||||||
|
|
||||||
|
if ( $skor > 0 && $bobot > 0 ) {
|
||||||
|
|
||||||
|
$nilai = $skor / $bobot * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(["point" => number_format($nilai, 2), "bobot" => $bobot, 'skor' => $test_passed->count()]);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// overview student
|
||||||
|
public function lecturer_overview_student( $topic_id, $user_id ) {
|
||||||
|
|
||||||
|
$enrollment = Enrollment::where('android_topic_id', $topic_id)->first();
|
||||||
|
$user = User::findOrFail( $user_id );
|
||||||
|
|
||||||
|
$where = array(
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
'user_id' => $user_id
|
||||||
|
);
|
||||||
|
$submit = Submits::where( $where )->get();
|
||||||
|
|
||||||
|
$dt_keseluruhan = array();
|
||||||
|
$NA = 0;
|
||||||
|
|
||||||
|
if ( $submit->count() > 0 ) {
|
||||||
|
|
||||||
|
$total_NA = 0;
|
||||||
|
$total_submit = $submit->count();
|
||||||
|
foreach ( $submit AS $isi ) {
|
||||||
|
|
||||||
|
|
||||||
|
// ambil data info nilai yang passed
|
||||||
|
$where_passed = [
|
||||||
|
|
||||||
|
'android_submit_id' => $isi->id,
|
||||||
|
'status_validate' => "passed"
|
||||||
|
];
|
||||||
|
// $test_passed = SubmitstestCase::where($where_passed)->get();
|
||||||
|
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||||
|
|
||||||
|
// ambil informasi data task untuk memanggil bobot
|
||||||
|
$info_testcase = Testcase::where("task_id", $isi->android_task_id)->get();
|
||||||
|
|
||||||
|
$bobot = 0;
|
||||||
|
// $dt = [];
|
||||||
|
foreach ( $info_testcase AS $isiit ) {
|
||||||
|
|
||||||
|
$bobot += $isiit->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skor = 0;
|
||||||
|
foreach ( $test_passed AS $isitp ) {
|
||||||
|
|
||||||
|
$skor += $isitp->score;
|
||||||
|
}
|
||||||
|
// hitung bobot per testcase
|
||||||
|
$nilai = 0;
|
||||||
|
if ( $skor > 0 && $bobot > 0 ) {
|
||||||
|
|
||||||
|
$nilai = $skor / $bobot * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ambil data testcase
|
||||||
|
$isi->testcase = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where('android_submit_id', $isi->id)->get();
|
||||||
|
$isi->nilai = $nilai;
|
||||||
|
$isi->bobot = $bobot;
|
||||||
|
|
||||||
|
// echo $nilai.' = '.($nilai / $bobot * 100);
|
||||||
|
// echo "<br>";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// autograding
|
||||||
|
$total_NA += $nilai;
|
||||||
|
// echo $nilai;
|
||||||
|
// echo "<hr>";
|
||||||
|
|
||||||
|
array_push($dt_keseluruhan, $isi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo $total_NA;
|
||||||
|
// echo " / ";
|
||||||
|
// echo $total_submit;
|
||||||
|
// echo " = ";
|
||||||
|
$NA = $total_NA / $total_submit;
|
||||||
|
// echo $total_NA;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// submission
|
||||||
|
$submission = SubmitsFinalSubmission::where( $where );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// echo $NA;
|
||||||
|
return view('android.teacher.overview_student', compact('enrollment', 'dt_keseluruhan', 'NA', 'user', 'submission'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function lecturer_confirm_student( $topic_id, $user_id, $enroll_id ){
|
||||||
|
|
||||||
|
$enrollment = Enrollment::findOrFail( $enroll_id );
|
||||||
|
|
||||||
|
$enrollment->status = "complete";
|
||||||
|
$enrollment->save();
|
||||||
|
|
||||||
|
return redirect("teacher/android23/overview-student/$topic_id/$user_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// notifikasi validator
|
||||||
|
public function notify_validator() {
|
||||||
|
|
||||||
|
$dt_notify = array();
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
// get submit topic
|
||||||
|
$submitTopic = Submits::where('validator', 'process')->groupBy('android_topic_id')->get();
|
||||||
|
|
||||||
|
|
||||||
|
foreach ( $submitTopic AS $isi ) {
|
||||||
|
|
||||||
|
$topic_id = $isi->android_topic_id;
|
||||||
|
$hitung = Submits::where('android_topic_id', $topic_id)->where('validator', 'process')->get()->count();
|
||||||
|
|
||||||
|
$total += $hitung;
|
||||||
|
|
||||||
|
$isi->waiting = $hitung;
|
||||||
|
array_push( $dt_notify, $isi );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'total' => $total,
|
||||||
|
'notify'=> $dt_notify
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
public function index() {
|
||||||
|
|
||||||
|
|
||||||
|
$dt_keseluruhan = array();
|
||||||
|
$topic = Topic::all();
|
||||||
|
|
||||||
|
if ( $topic->count() > 0 ) {
|
||||||
|
|
||||||
|
foreach ( $topic AS $isi ) {
|
||||||
|
|
||||||
|
// count data task
|
||||||
|
$isi->total = Task::where('android_topic_id', $isi->id)->count();
|
||||||
|
array_push( $dt_keseluruhan, $isi );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('android.admin.topic.index', compact('dt_keseluruhan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// tambah topik
|
||||||
|
function add(Request $request) {
|
||||||
|
|
||||||
|
$nama_file = "";
|
||||||
|
$directory_upload = "android23/profile";
|
||||||
|
|
||||||
|
|
||||||
|
// cek direktori
|
||||||
|
$direktori = 'android23/document/'.$request->folder_path;
|
||||||
|
if ( !is_dir( $direktori ) ){
|
||||||
|
|
||||||
|
mkdir( $direktori );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $request->hasFile('picturePath') ) {
|
||||||
|
|
||||||
|
$file = $request->file('picturePath');
|
||||||
|
$nama_file = $file->getClientOriginalName();
|
||||||
|
$file->move($directory_upload, $nama_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'title' => $request->title,
|
||||||
|
'description' => $request->description,
|
||||||
|
'folder_path' => $request->folder_path,
|
||||||
|
'picturePath' => $nama_file,
|
||||||
|
'status' => $request->status
|
||||||
|
);
|
||||||
|
|
||||||
|
Topic::insert($data);
|
||||||
|
|
||||||
|
return redirect('android23/topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update topik
|
||||||
|
function update( Request $request, $id ) {
|
||||||
|
|
||||||
|
$topik = Topic::where("id", $id)->first();
|
||||||
|
$nama_file = $topik->picturePath;
|
||||||
|
|
||||||
|
$directory_upload = "android23/profile";
|
||||||
|
|
||||||
|
if ( $request->hasFile('picturePath') ) {
|
||||||
|
|
||||||
|
$file = $request->file('picturePath');
|
||||||
|
$nama_file = $file->getClientOriginalName();
|
||||||
|
$file->move($directory_upload, $nama_file);
|
||||||
|
|
||||||
|
|
||||||
|
// delete old pic
|
||||||
|
if ( !empty( $topik->picturePath ) ) {
|
||||||
|
|
||||||
|
unlink( $directory_upload .'/'. $topik->picturePath );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'title' => $request->title,
|
||||||
|
'description' => $request->description,
|
||||||
|
'folder_path' => $request->folder_path,
|
||||||
|
'picturePath' => $nama_file,
|
||||||
|
'status' => $request->status
|
||||||
|
);
|
||||||
|
|
||||||
|
Topic::where('id', $id)->update($data);
|
||||||
|
return redirect('android23/topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// delete
|
||||||
|
function delete( $id ) {
|
||||||
|
|
||||||
|
Topic::where("id", $id)->delete();
|
||||||
|
return redirect('android23/topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// learning task
|
||||||
|
function learning_view( $id ) {
|
||||||
|
|
||||||
|
$topic = Topic::where('id', $id)->first();
|
||||||
|
$dt_task = Task::where('android_topic_id', $id)->get();
|
||||||
|
|
||||||
|
// sum bobot keseluruhan
|
||||||
|
// $total = Task::where('android_topic_id', $id)->sum('bobot');
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
$task = array();
|
||||||
|
foreach ( $dt_task AS $isi ) {
|
||||||
|
|
||||||
|
// ambil data testcase
|
||||||
|
$testcase = Testcase::where("task_id", $isi->id)->get();
|
||||||
|
$isi->testcase = $testcase;
|
||||||
|
|
||||||
|
array_push( $task, $isi );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// print_r( $total );
|
||||||
|
return view('android.admin.topic.learning', compact('topic', 'task', 'total'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// tambah learnng
|
||||||
|
function learning_add( Request $request, $id, $tipe ) {
|
||||||
|
|
||||||
|
$topic = Topic::where('id', $id)->first();
|
||||||
|
$nama_file = "";
|
||||||
|
$directory_upload = "android23/document/". $topic->folder_path;
|
||||||
|
|
||||||
|
if ( $request->hasFile('material') ) {
|
||||||
|
|
||||||
|
$file = $request->file('material');
|
||||||
|
$nama_file = $file->getClientOriginalName();
|
||||||
|
$file->move($directory_upload, $nama_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert data task
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'android_topic_id' => $id,
|
||||||
|
'task_no' => $request->task_no,
|
||||||
|
'task_name' => $request->title,
|
||||||
|
'caption' => $request->caption,
|
||||||
|
'material' => $nama_file,
|
||||||
|
'tipe' => $tipe,
|
||||||
|
// 'testcase' => implode(',', $tags)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$id_task = Task::create($data)->id;
|
||||||
|
|
||||||
|
|
||||||
|
// cek apakah mengisi data
|
||||||
|
|
||||||
|
|
||||||
|
$dt_testcase = array();
|
||||||
|
if ( $request->has('tags') && $tipe == "submission" ){
|
||||||
|
|
||||||
|
// convert tags to obj
|
||||||
|
$data_tags = json_decode($request->tags);
|
||||||
|
|
||||||
|
foreach ( $data_tags AS $val ){
|
||||||
|
|
||||||
|
array_push( $dt_testcase, [
|
||||||
|
|
||||||
|
'task_id' => $id_task,
|
||||||
|
'case' => $val->value,
|
||||||
|
'score' => 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// print_r( $dt_testcase );
|
||||||
|
// insert data testcase
|
||||||
|
Testcase::insert( $dt_testcase );
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect('android23/topic/learning/'. $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function learning_update( Request $request, $id_topic, $id_task ) {
|
||||||
|
|
||||||
|
$topik = Topic::where("id", $id_topic)->first();
|
||||||
|
$task = Task::where("id", $id_task)->first();
|
||||||
|
$nama_file = $task->material;
|
||||||
|
|
||||||
|
$directory_upload = "android23/document/$topik->folder_path";
|
||||||
|
|
||||||
|
if ( $request->hasFile('material') ) {
|
||||||
|
|
||||||
|
$file = $request->file('material');
|
||||||
|
$nama_file = $file->getClientOriginalName();
|
||||||
|
$file->move($directory_upload, $nama_file);
|
||||||
|
|
||||||
|
|
||||||
|
// delete old pic
|
||||||
|
if ( !empty( $task->material ) ) {
|
||||||
|
|
||||||
|
unlink( $directory_upload .'/'. $task->material );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'task_no' => $request->task_no,
|
||||||
|
'task_name' => $request->title,
|
||||||
|
'caption' => $request->caption,
|
||||||
|
'material' => $nama_file,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
Task::where('id', $id_task)->update($data);
|
||||||
|
return redirect('android23/topic/learning/'. $id_topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function learning_remove( $id_topic, $id_task ) {
|
||||||
|
|
||||||
|
// remove data testcase
|
||||||
|
Testcase::where('task_id', $id_task)->delete();
|
||||||
|
|
||||||
|
Task::where('id', $id_task)->delete();
|
||||||
|
return redirect('android23/topic/learning/'. $id_topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Testcase */
|
||||||
|
public function learning_update_testcase( Request $request ) {
|
||||||
|
|
||||||
|
// task id
|
||||||
|
$task_id = $request->task_id;
|
||||||
|
|
||||||
|
$dt_testcase_baru = array();
|
||||||
|
|
||||||
|
foreach ( $request->case AS $index => $isi ) {
|
||||||
|
|
||||||
|
array_push( $dt_testcase_baru, [
|
||||||
|
|
||||||
|
'task_id' => $task_id,
|
||||||
|
'case' => $isi,
|
||||||
|
'score' => $request->score[$index]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// hapus data testcase lama
|
||||||
|
Testcase::where('task_id', $task_id)->delete();
|
||||||
|
Testcase::insert( $dt_testcase_baru );
|
||||||
|
|
||||||
|
return redirect('android23/topic/learning/'. $request->topic_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function learning_add_testcase( Request $request, $topic_id, $task_id ) {
|
||||||
|
|
||||||
|
$dt_testcase = array();
|
||||||
|
if ( $request->has('tags') ){
|
||||||
|
|
||||||
|
// convert tags to obj
|
||||||
|
$data_tags = json_decode($request->tags);
|
||||||
|
|
||||||
|
foreach ( $data_tags AS $val ){
|
||||||
|
|
||||||
|
array_push( $dt_testcase, [
|
||||||
|
|
||||||
|
'task_id' => $task_id,
|
||||||
|
'case' => $val->value,
|
||||||
|
'score' => 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert data testcase
|
||||||
|
Testcase::insert( $dt_testcase );
|
||||||
|
}
|
||||||
|
return redirect('android23/topic/learning/'. $topic_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function learning_reset_testcase( $topic_id, $task_id ) {
|
||||||
|
|
||||||
|
Testcase::where('task_id', $task_id)->delete();
|
||||||
|
return redirect('android23/topic/learning/'. $topic_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function learning_remove_testcase( $topic_id, $testcase_id ){
|
||||||
|
|
||||||
|
$testcase = Testcase::find( $testcase_id );
|
||||||
|
$testcase->delete();
|
||||||
|
|
||||||
|
return redirect('android23/topic/learning/'. $topic_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
11
iClOP-V2/app/Http/Controllers/Android/AssignController.php
Normal file
11
iClOP-V2/app/Http/Controllers/Android/AssignController.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Android23;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class AssignController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
42
iClOP-V2/app/Http/Controllers/Android/EnrollController.php
Normal file
42
iClOP-V2/app/Http/Controllers/Android/EnrollController.php
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Android;
|
||||||
|
|
||||||
|
use App\Models\Android\Enrollment;
|
||||||
|
use App\Models\Android\Topic;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
class EnrollController extends Controller
|
||||||
|
{
|
||||||
|
public function enroll( Request $request, $topic_id = null ){
|
||||||
|
|
||||||
|
// cek
|
||||||
|
$topik = Topic::findOrFail($topic_id);
|
||||||
|
|
||||||
|
$where = ['android_topic_id' => $topic_id, 'user_id' => Auth::user()->id];
|
||||||
|
$enroll_status = Enrollment::where($where)->count();
|
||||||
|
|
||||||
|
if ( $enroll_status == 0 ) {
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'status' => "process"
|
||||||
|
);
|
||||||
|
|
||||||
|
Enrollment::create( $data );
|
||||||
|
return redirect()->route('material');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// sebelumnya sudah pernah melakukan enroll
|
||||||
|
return redirect()->route('material')->withErrors('pesan', 'Invalid Enrollment Request');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
670
iClOP-V2/app/Http/Controllers/Android/MaterialController.php
Normal file
670
iClOP-V2/app/Http/Controllers/Android/MaterialController.php
Normal file
|
|
@ -0,0 +1,670 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Android;
|
||||||
|
|
||||||
|
use App\Models\Android\Enrollment;
|
||||||
|
use App\Models\Android\Submits;
|
||||||
|
use App\Models\Android\SubmitsTestCase;
|
||||||
|
use App\Models\Android\SubmitsFinalSubmission;
|
||||||
|
use App\Models\Android\Task;
|
||||||
|
use App\Models\Android\Task_waiting;
|
||||||
|
use App\Models\Android\Topic;
|
||||||
|
use App\Models\Android\Testcase;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class MaterialController extends Controller
|
||||||
|
{
|
||||||
|
public function index() {
|
||||||
|
|
||||||
|
$topic = DB::table("android_topics");
|
||||||
|
$data_keseluruhan = array();
|
||||||
|
$id = Auth::user()->id;
|
||||||
|
|
||||||
|
$recent_task = array();
|
||||||
|
|
||||||
|
if ( $topic->count() > 0 ) {
|
||||||
|
|
||||||
|
foreach ( $topic->get() AS $isi ){
|
||||||
|
|
||||||
|
// init
|
||||||
|
$status = "enroll";
|
||||||
|
$progress = 0;
|
||||||
|
$grade = "-";
|
||||||
|
$total_task = Task::where("android_topic_id", $isi->id)->count();
|
||||||
|
$recent = "-";
|
||||||
|
$akses = "-";
|
||||||
|
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'android_topic_id' => $isi->id,
|
||||||
|
'user_id' => $id
|
||||||
|
);
|
||||||
|
|
||||||
|
// check
|
||||||
|
$enrollment = DB::table("android_enrollment")->where($where);
|
||||||
|
if ( $enrollment->count() != 0 ) {
|
||||||
|
|
||||||
|
$dt_enroll = $enrollment->first();
|
||||||
|
$status = $dt_enroll->status;
|
||||||
|
|
||||||
|
$akses = $enrollment->first()->created_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check progress
|
||||||
|
if ( $status != "cancel" ) {
|
||||||
|
|
||||||
|
$task_waiting = Task_waiting::where( $where );
|
||||||
|
|
||||||
|
$total_task_waiting = $task_waiting->count();
|
||||||
|
if ( $total_task_waiting > 0 ) {
|
||||||
|
|
||||||
|
$info = Task::find( $task_waiting->first()->android_task_id );
|
||||||
|
$progress = $total_task_waiting / $total_task * 100;
|
||||||
|
|
||||||
|
$recent = $info->task_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$NA = 0;
|
||||||
|
if ( $status == "complete" ) {
|
||||||
|
|
||||||
|
$where_enroll = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $isi->id
|
||||||
|
);
|
||||||
|
$enrollment = Enrollment::where($where_enroll)->first();
|
||||||
|
|
||||||
|
|
||||||
|
$dt_all_submit = array();
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id, // 1577
|
||||||
|
'android_submits.android_topic_id' => $isi->id // 11
|
||||||
|
);
|
||||||
|
$submits = Submits::select('android_submits.id','android_task.task_name', 'android_submits.created_at', 'android_submits.duration')
|
||||||
|
->join('android_task', 'android_task.id', '=', 'android_submits.android_task_id')->where($where)->get();
|
||||||
|
|
||||||
|
|
||||||
|
//mulai sini autograding
|
||||||
|
|
||||||
|
$estimate = 0;
|
||||||
|
$total_submit = $submits->count();
|
||||||
|
|
||||||
|
if ( $submits->count() > 0 ) {
|
||||||
|
|
||||||
|
$total_test = 0;
|
||||||
|
|
||||||
|
foreach ( $submits AS $isi_sb ) {
|
||||||
|
|
||||||
|
$submit_id = $isi_sb->id;
|
||||||
|
|
||||||
|
|
||||||
|
$where_passed = [
|
||||||
|
|
||||||
|
'android_submit_id' => $isi_sb->id,
|
||||||
|
'status_validate' => "passed"
|
||||||
|
];
|
||||||
|
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||||
|
|
||||||
|
// ambil informasi data task untuk memanggil bobot
|
||||||
|
$info_testcase = Testcase::where("task_id", $isi_sb->android_task_id)->get();
|
||||||
|
|
||||||
|
$bobot = 0;
|
||||||
|
foreach ( $info_testcase AS $isiit ) {
|
||||||
|
|
||||||
|
$bobot += $isiit->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skor = 0;
|
||||||
|
foreach ( $test_passed AS $isitp ) {
|
||||||
|
|
||||||
|
$skor += $isitp->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nilai = 0;
|
||||||
|
if ( $skor > 0 && $bobot > 0 ) {
|
||||||
|
|
||||||
|
$nilai = $skor / $bobot * 100;
|
||||||
|
// echo "masuk";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
// $dt_submit_testcase = array();
|
||||||
|
|
||||||
|
// $testcase = SubmitsTestCase::where("android_submit_id", $submit_id)->get();
|
||||||
|
|
||||||
|
// // autograding
|
||||||
|
// $passed = 0;
|
||||||
|
// foreach ( $testcase AS $det ) {
|
||||||
|
|
||||||
|
// if ( $det->status_validate == "passed" ) {
|
||||||
|
|
||||||
|
// $passed++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $persentage = ($passed / $testcase->count() * 100);
|
||||||
|
$total_test += $nilai;
|
||||||
|
$estimate += $isi_sb->duration;
|
||||||
|
|
||||||
|
|
||||||
|
// echo $nilai.'<br>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$NA = $total_test / $total_submit;
|
||||||
|
|
||||||
|
|
||||||
|
// echo $total_test.' - '. $total_submit;
|
||||||
|
// echo $NA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$isi->status_waiting = $status;
|
||||||
|
$isi->progress = $progress;
|
||||||
|
$isi->grade = $grade;
|
||||||
|
$isi->total_task = $total_task;
|
||||||
|
$isi->akses_materi = $akses;
|
||||||
|
$isi->recent = $recent;
|
||||||
|
$isi->NA = $NA;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// recent task
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'android_task.android_topic_id' => $isi->id,
|
||||||
|
'user_id' => $id
|
||||||
|
);
|
||||||
|
$recent_task = Task_waiting::select("task_name", "title")
|
||||||
|
->join("android_task", "android_task.id", "=", "android_task_waiting.android_task_id")
|
||||||
|
->join("android_topics", "android_topics.id", "=", "android_task_waiting.android_topic_id")
|
||||||
|
->where( $where )->orderBy('android_task.created_at', 'desc')->get()->unique('android_topic_id');
|
||||||
|
|
||||||
|
array_push( $data_keseluruhan, $isi );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return view('android.student.material.index', compact('data_keseluruhan', 'recent_task'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// upload
|
||||||
|
public function upload( Request $request ) {
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
|
||||||
|
'file' => 'required'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fileName = time().'-'.request()->file->getClientOriginalExtension();
|
||||||
|
request()->file->move(public_path('files'), $fileName);
|
||||||
|
|
||||||
|
return request()->json([
|
||||||
|
'success' => "You have successfully upload file"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function task( Request $request, $topic_id ) {
|
||||||
|
|
||||||
|
// ambil data informasi topik
|
||||||
|
$topic = Topic::findOrFail( $topic_id );
|
||||||
|
$where = array(
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_task_id' => $request->id
|
||||||
|
);
|
||||||
|
$submit_byId = Submits::where($where);
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
|
||||||
|
// cek memiliki id atau tidak
|
||||||
|
if ($request->filled('id')) {
|
||||||
|
|
||||||
|
$id_task_recommend = $request->id;
|
||||||
|
|
||||||
|
// ambil informasi data task berdasarkan id
|
||||||
|
$task = Task::findOrFail($id_task_recommend);
|
||||||
|
$testcase = Testcase::where("task_id", $id_task_recommend)->get();
|
||||||
|
|
||||||
|
// $task = Task::where('id', $id_task_recommend);
|
||||||
|
|
||||||
|
|
||||||
|
$submit_information = array();
|
||||||
|
$submit_testcase = array();
|
||||||
|
if ( $submit_byId->count() > 0 ) {
|
||||||
|
|
||||||
|
$submits = $submit_byId->first();
|
||||||
|
|
||||||
|
$submitTestCase = SubmitsTestCase::where("android_submit_id", $submits->id)->get();
|
||||||
|
|
||||||
|
$submit_information = $submits;
|
||||||
|
$submit_testcase = $submitTestCase;
|
||||||
|
|
||||||
|
|
||||||
|
// echo json_encode($submits->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// cek apakah sudah mengakses materi ?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// next material
|
||||||
|
$next = Task::where('id', '>', $task->id);
|
||||||
|
|
||||||
|
if ( $next->count() > 0 ) {
|
||||||
|
|
||||||
|
$url = '/android23/task/'.$topic_id.'?id='. $next->min('id');
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$url = '/android23/task/'.$topic_id.'?id='. $id_task_recommend.'&type=final';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$dt_task = array();
|
||||||
|
$tasks = Task::where("android_topic_id", $topic_id)->orderBy('task_no', 'ASC')->get();
|
||||||
|
|
||||||
|
foreach ( $tasks AS $isi_tsk ) {
|
||||||
|
|
||||||
|
$where = [
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
'android_task_id' => $isi_tsk->id,
|
||||||
|
|
||||||
|
];
|
||||||
|
$task_waiting = Task_waiting::where($where)->get();
|
||||||
|
|
||||||
|
if ( $task_waiting->count() == 0 ) {
|
||||||
|
|
||||||
|
$isi_tsk->status_akses = false;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$isi_tsk->status_akses = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push( $dt_task, $isi_tsk );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'topic_id' => $topic_id,
|
||||||
|
'id' => $id_task_recommend,
|
||||||
|
'task' => $task,
|
||||||
|
'testcase' => $testcase,
|
||||||
|
'topic' => $topic,
|
||||||
|
'all_task' => $dt_task,
|
||||||
|
'submit_information' => $submit_information,
|
||||||
|
'submit_testcase' => $submit_testcase,
|
||||||
|
'request' => $request,
|
||||||
|
'taskwaiting' => $task_waiting->count(),
|
||||||
|
'urlku' => $url
|
||||||
|
);
|
||||||
|
|
||||||
|
// echo $url;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// cek apabila request sudah final
|
||||||
|
if ( $request->type == "final" ) {
|
||||||
|
|
||||||
|
$where_enroll = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $topic_id
|
||||||
|
);
|
||||||
|
$enrollment = Enrollment::where($where_enroll)->first();
|
||||||
|
|
||||||
|
|
||||||
|
$dt_all_submit = array();
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id, // 1577
|
||||||
|
'android_submits.android_topic_id' => $topic_id // 11
|
||||||
|
);
|
||||||
|
$submits = Submits::select('android_task_id','android_submits.id','android_task.task_name', 'android_submits.created_at', 'android_submits.duration')
|
||||||
|
->join('android_task', 'android_task.id', '=', 'android_submits.android_task_id')->where($where)->get();
|
||||||
|
|
||||||
|
|
||||||
|
//mulai sini autograding
|
||||||
|
$NA = 0;
|
||||||
|
$estimate = 0;
|
||||||
|
$total_submit = $submits->count();
|
||||||
|
|
||||||
|
if ( $submits->count() > 0 ) {
|
||||||
|
|
||||||
|
$total_test = 0;
|
||||||
|
|
||||||
|
foreach ( $submits AS $isi ) {
|
||||||
|
|
||||||
|
$submit_id = $isi->id;
|
||||||
|
$dt_submit_testcase = array();
|
||||||
|
|
||||||
|
$testcase = SubmitsTestCase::where("android_submit_id", $submit_id)->get();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// autograding
|
||||||
|
$where_passed = [
|
||||||
|
|
||||||
|
'android_submit_id' => $isi->id,
|
||||||
|
'status_validate' => "passed"
|
||||||
|
];
|
||||||
|
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||||
|
|
||||||
|
// ambil informasi data task untuk memanggil bobot
|
||||||
|
$info_testcase = Testcase::where("task_id", $isi->android_task_id)->get();
|
||||||
|
|
||||||
|
$bobot = 0;
|
||||||
|
foreach ( $info_testcase AS $isiit ) {
|
||||||
|
|
||||||
|
$bobot += $isiit->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skor = 0;
|
||||||
|
foreach ( $test_passed AS $isitp ) {
|
||||||
|
|
||||||
|
$skor += $isitp->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nilai = 0;
|
||||||
|
if ( $skor > 0 && $bobot > 0 ) {
|
||||||
|
|
||||||
|
$nilai = $skor / $bobot * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
$total_test += $nilai;
|
||||||
|
|
||||||
|
|
||||||
|
// echo $isi->android_task_id.'<br>';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// autograding (old)
|
||||||
|
// $passed = 0;
|
||||||
|
// foreach ( $testcase AS $det ) {
|
||||||
|
|
||||||
|
// if ( $det->status_validate == "passed" ) {
|
||||||
|
|
||||||
|
// $passed++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $persentage = ($passed / $testcase->count() * 100);
|
||||||
|
// $total_test += $persentage;
|
||||||
|
// total_test = total_NA + persentage
|
||||||
|
// total_test = 0 + 57
|
||||||
|
// total_test = 57
|
||||||
|
|
||||||
|
// total_test = total_test + persentnage
|
||||||
|
// total test = 57 + 75
|
||||||
|
$estimate += $isi->duration;
|
||||||
|
|
||||||
|
array_push( $dt_all_submit, [
|
||||||
|
|
||||||
|
'info' => $isi,
|
||||||
|
'persentage' => $nilai,
|
||||||
|
'testcase' => $testcase
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$NA = $total_test / $total_submit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$add = array(
|
||||||
|
|
||||||
|
'submits' => $dt_all_submit,
|
||||||
|
'NA' => $NA,
|
||||||
|
'submit_submission' => SubmitsFinalSubmission::all(),
|
||||||
|
'enrollment' => $enrollment,
|
||||||
|
'estimate' => $estimate
|
||||||
|
);
|
||||||
|
|
||||||
|
// merge
|
||||||
|
$data = array_merge($data, $add);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return view
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// last recomend
|
||||||
|
$id_task_recommend = "";
|
||||||
|
$where = [
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
];
|
||||||
|
$recommend = Task_waiting::where( $where )->orderBy('created_at', 'DESC');
|
||||||
|
if ( $recommend->count() == 0 ) {
|
||||||
|
|
||||||
|
// memberikan id awal
|
||||||
|
// ambil materi awal task berdasarkan topic id
|
||||||
|
$dt_task = Task::where('android_topic_id', $topic_id)->first();
|
||||||
|
$id_task_recommend = $dt_task->id;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$dt_task = $recommend->first();
|
||||||
|
$id_task_recommend = $dt_task->android_task_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo "test";
|
||||||
|
// redirect . ..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// access materi update
|
||||||
|
$cektaskwaiting = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
'android_task_id' => $id_task_recommend
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// cek
|
||||||
|
$cekTask = Task_waiting::where( $cektaskwaiting )->get();
|
||||||
|
if ( $cekTask->count() == 0 ) {
|
||||||
|
|
||||||
|
Task_waiting::create( $cektaskwaiting );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// print_r( $data );
|
||||||
|
if ( $request->filled('id') ) {
|
||||||
|
|
||||||
|
return view('android.student/material/task', $data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return redirect('android23/task/'. $topic_id.'?id='. $id_task_recommend);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//validation result
|
||||||
|
public function validation() {
|
||||||
|
|
||||||
|
// informasi login sebagai
|
||||||
|
$id = Auth::user()->id;
|
||||||
|
|
||||||
|
$kondisi = ["user_id" => $id, 'status' => "complete"];
|
||||||
|
$enrollment = Enrollment::where($kondisi)->get();
|
||||||
|
|
||||||
|
$dt_keseluruhan = array();
|
||||||
|
|
||||||
|
if ( $enrollment->count() > 0 ) {
|
||||||
|
|
||||||
|
foreach ( $enrollment AS $isi ) {
|
||||||
|
|
||||||
|
// id topik
|
||||||
|
$topic_id = $isi->android_topic_id;
|
||||||
|
$topic = Topic::find( $topic_id );
|
||||||
|
|
||||||
|
$where_task = ["android_topic_id" => $topic_id, 'user_id' => $id];
|
||||||
|
// $task = Task::where( $where_task )->get();
|
||||||
|
|
||||||
|
// ambil data submits
|
||||||
|
$submit = Submits::where($where_task)->get();
|
||||||
|
|
||||||
|
$isi->submit = $submit;
|
||||||
|
$isi->topic = $topic;
|
||||||
|
// $isi->task = $task;
|
||||||
|
|
||||||
|
|
||||||
|
array_push( $dt_keseluruhan, $isi );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo "oke";
|
||||||
|
return view('android.student.validation.index', compact('dt_keseluruhan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function validation_detail( $topic_id ) {
|
||||||
|
|
||||||
|
$enrollment = Enrollment::where( "android_topic_id", $topic_id )->first();
|
||||||
|
|
||||||
|
|
||||||
|
$dt_all_submit = array();
|
||||||
|
$where = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id, // 1577
|
||||||
|
'android_submits.android_topic_id' => $topic_id // 11
|
||||||
|
);
|
||||||
|
$submits = Submits::select('android_task_id','android_submits.id','android_task.task_name', 'android_submits.created_at', 'android_submits.duration')
|
||||||
|
->join('android_task', 'android_task.id', '=', 'android_submits.android_task_id')->where($where)->get();
|
||||||
|
|
||||||
|
|
||||||
|
//mulai sini autograding
|
||||||
|
$NA = 0;
|
||||||
|
$estimate = 0;
|
||||||
|
$total_submit = $submits->count();
|
||||||
|
|
||||||
|
if ( $submits->count() > 0 ) {
|
||||||
|
|
||||||
|
$total_test = 0;
|
||||||
|
|
||||||
|
foreach ( $submits AS $isi ) {
|
||||||
|
|
||||||
|
$submit_id = $isi->id;
|
||||||
|
$dt_submit_testcase = array();
|
||||||
|
|
||||||
|
$testcase = SubmitsTestCase::where("android_submit_id", $submit_id)->get();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// autograding
|
||||||
|
$where_passed = [
|
||||||
|
|
||||||
|
'android_submit_id' => $isi->id,
|
||||||
|
'status_validate' => "passed"
|
||||||
|
];
|
||||||
|
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||||
|
|
||||||
|
// ambil informasi data task untuk memanggil bobot
|
||||||
|
$info_testcase = Testcase::where("task_id", $isi->android_task_id)->get();
|
||||||
|
|
||||||
|
$bobot = 0;
|
||||||
|
foreach ( $info_testcase AS $isiit ) {
|
||||||
|
|
||||||
|
$bobot += $isiit->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skor = 0;
|
||||||
|
foreach ( $test_passed AS $isitp ) {
|
||||||
|
|
||||||
|
$skor += $isitp->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nilai = 0;
|
||||||
|
if ( $skor > 0 && $bobot > 0 ) {
|
||||||
|
|
||||||
|
$nilai = $skor / $bobot * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
$total_test += $nilai;
|
||||||
|
|
||||||
|
|
||||||
|
// echo $isi->android_task_id.'<br>';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// autograding (old)
|
||||||
|
// $passed = 0;
|
||||||
|
// foreach ( $testcase AS $det ) {
|
||||||
|
|
||||||
|
// if ( $det->status_validate == "passed" ) {
|
||||||
|
|
||||||
|
// $passed++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $persentage = ($passed / $testcase->count() * 100);
|
||||||
|
// $total_test += $persentage;
|
||||||
|
// total_test = total_NA + persentage
|
||||||
|
// total_test = 0 + 57
|
||||||
|
// total_test = 57
|
||||||
|
|
||||||
|
// total_test = total_test + persentnage
|
||||||
|
// total test = 57 + 75
|
||||||
|
$estimate += $isi->duration;
|
||||||
|
|
||||||
|
array_push( $dt_all_submit, [
|
||||||
|
|
||||||
|
'info' => $isi,
|
||||||
|
'persentage' => $nilai,
|
||||||
|
'testcase' => $testcase
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$NA = $total_test / $total_submit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dt_keseluruhan = array(
|
||||||
|
|
||||||
|
'submits' => $dt_all_submit,
|
||||||
|
'NA' => $NA,
|
||||||
|
'submit_submission' => SubmitsFinalSubmission::all(),
|
||||||
|
'enrollment' => $enrollment,
|
||||||
|
'estimate' => $estimate
|
||||||
|
);
|
||||||
|
|
||||||
|
return view('android.student.validation.detail', compact('dt_keseluruhan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
171
iClOP-V2/app/Http/Controllers/Android/SubmissionController.php
Normal file
171
iClOP-V2/app/Http/Controllers/Android/SubmissionController.php
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Android;
|
||||||
|
|
||||||
|
use App\Models\Android\Submits;
|
||||||
|
use App\Models\Android\SubmitsTestCase;
|
||||||
|
use App\Models\Android\Enrollment;
|
||||||
|
use App\Models\Android\SubmitsFinalSubmission;
|
||||||
|
use App\Models\Android\Topic;
|
||||||
|
use App\Models\Android\Task;
|
||||||
|
use App\Models\Android\Testcase;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class SubmissionController extends Controller
|
||||||
|
{
|
||||||
|
public function proses_tambah( Request $request ) {
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
|
||||||
|
'userfile' => 'required|image',
|
||||||
|
'duration' => 'required',
|
||||||
|
'comment' => 'required|string'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$file = $request->file('userfile');
|
||||||
|
$extension = $file->getClientOriginalExtension();
|
||||||
|
|
||||||
|
// upload path
|
||||||
|
$upload_path = "android23/submission";
|
||||||
|
$fileName = uniqid().'-'.strtotime("now").'.'.$extension;
|
||||||
|
|
||||||
|
$file->move($upload_path, $fileName);
|
||||||
|
$task_id = $request->android_task_id;
|
||||||
|
$topic_id = $request->android_topic_id;
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
'android_task_id' => $task_id,
|
||||||
|
'duration' => $request->duration,
|
||||||
|
'upload' => $fileName,
|
||||||
|
'comment' => $request->comment,
|
||||||
|
'validator' => "process"
|
||||||
|
);
|
||||||
|
|
||||||
|
$submit = Submits::create( $data );
|
||||||
|
|
||||||
|
$dt_task = Task::findOrFail( $task_id );
|
||||||
|
$dt_testcasefromtask = Testcase::where("task_id", $task_id)->get();
|
||||||
|
|
||||||
|
|
||||||
|
// listdata test case
|
||||||
|
$data_submit = array();
|
||||||
|
|
||||||
|
if ( $request->has('task') ) {
|
||||||
|
foreach ( $dt_testcasefromtask AS $isi ) {
|
||||||
|
|
||||||
|
$status = false;
|
||||||
|
foreach ( $request->task AS $ts ){
|
||||||
|
|
||||||
|
if ( $ts == $isi->id ) {
|
||||||
|
|
||||||
|
$status = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = "failed";
|
||||||
|
if ( $status ) {
|
||||||
|
|
||||||
|
$label = "passed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
array_push($data_submit, [
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_submit_id' => $submit->id,
|
||||||
|
'android_testcase_id'=> $isi->id,
|
||||||
|
'status'=> $label,
|
||||||
|
'status_validate' => 'waiting',
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'updated_at' => Carbon::now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_r( $data_submit );
|
||||||
|
SubmitsTestCase::insert($data_submit);
|
||||||
|
}
|
||||||
|
return redirect('android23/task/'.$topic_id.'?id='.$task_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// submit final submission
|
||||||
|
public function submit_final_submission( Request $request, $topic_id ){
|
||||||
|
|
||||||
|
|
||||||
|
if ( !empty( $request->type ) ) {
|
||||||
|
// validation
|
||||||
|
if ( $request->type == "github" ) {
|
||||||
|
|
||||||
|
$rules = array(
|
||||||
|
'link' => 'required|string'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$rules = array(
|
||||||
|
'userfile' => 'required|file|mimes:zip|max:3072'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check rules
|
||||||
|
$request->validate($rules);
|
||||||
|
|
||||||
|
if ( $request->type == "zip" ) {
|
||||||
|
|
||||||
|
// file upload
|
||||||
|
$file = $request->file('userfile');
|
||||||
|
|
||||||
|
// upload path
|
||||||
|
$upload_path = "android23/final-submission";
|
||||||
|
$userfile = uniqid().'-'.strtotime("now").'.zip';
|
||||||
|
|
||||||
|
$file->move($upload_path, $userfile);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// github
|
||||||
|
$userfile = $request->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
|
||||||
|
'user_id' => Auth::user()->id,
|
||||||
|
'android_topic_id' => $topic_id,
|
||||||
|
'tipe' => $request->type,
|
||||||
|
'userfile' => $userfile
|
||||||
|
);
|
||||||
|
|
||||||
|
SubmitsFinalSubmission::create( $data );
|
||||||
|
|
||||||
|
|
||||||
|
$dataEnrollUpdate = array(
|
||||||
|
|
||||||
|
'status' => "review"
|
||||||
|
);
|
||||||
|
Enrollment::where(['user_id' => $data['user_id'], 'android_topic_id' => $data['android_topic_id']])->update( $dataEnrollUpdate );
|
||||||
|
|
||||||
|
// redirect
|
||||||
|
return redirect('android23/task/'.$topic_id.'?id='.$request->task_id.'&type=final');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function overview( $id ) {
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'topic_id' => $id,
|
||||||
|
'topic' => Topic::findOrFail( $id ),
|
||||||
|
'all_task' => Task::orderBy('task_no', 'ASC')->get(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return view('student.android23.material.overview', $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
126
iClOP-V2/app/Http/Controllers/AuthController.php
Normal file
126
iClOP-V2/app/Http/Controllers/AuthController.php
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
|
||||||
|
class AuthController extends Controller
|
||||||
|
{
|
||||||
|
public function proses(Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
'email' => 'required',
|
||||||
|
'password' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$credential = request(['email', 'password']);
|
||||||
|
if (Auth::attempt($credential)) {
|
||||||
|
|
||||||
|
if (Auth::user()->role == "admin") {
|
||||||
|
return redirect('welcome');
|
||||||
|
|
||||||
|
} else if (Auth::user()->role == "teacher") {
|
||||||
|
return redirect('dashboard_teacher');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// student
|
||||||
|
session(['key' => Auth::user()->name]);
|
||||||
|
session(['email' => Auth::user()->email]);
|
||||||
|
return redirect('dashboard-student');
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
echo "okee err";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signup(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'name' => 'required',
|
||||||
|
'email' => 'required',
|
||||||
|
'password' => 'required|confirmed',
|
||||||
|
'role' => 'required',
|
||||||
|
|
||||||
|
]);
|
||||||
|
$data['password'] = bcrypt($data['password']);
|
||||||
|
User::create($data);
|
||||||
|
|
||||||
|
return redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logoutt(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
Auth::logout();
|
||||||
|
$request->session()->invalidate();
|
||||||
|
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
return redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redirect()
|
||||||
|
{
|
||||||
|
return Socialite::driver(driver: 'google')->redirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function googleCallback()
|
||||||
|
{
|
||||||
|
$user = Socialite::driver('google')->user();
|
||||||
|
$userDatabase = User::where('google_id', $user->getId())->first();
|
||||||
|
$token = $user->token;
|
||||||
|
session(['google_token' => $token]);
|
||||||
|
|
||||||
|
if (!$userDatabase) {
|
||||||
|
$data = [
|
||||||
|
'google_id' => $user->getId(),
|
||||||
|
'name' => $user->getName(),
|
||||||
|
'email' => $user->getEmail(),
|
||||||
|
'role' => 'student',
|
||||||
|
];
|
||||||
|
$existingUser = User::where('email', $data['email'])->first();
|
||||||
|
if ($existingUser) {
|
||||||
|
$existingUser->update($data);
|
||||||
|
$userDatabase = $existingUser;
|
||||||
|
} else {
|
||||||
|
$userDatabase = User::create($data);
|
||||||
|
}
|
||||||
|
auth()->login($userDatabase);
|
||||||
|
|
||||||
|
session()->regenerate();
|
||||||
|
return redirect()->route('dashboard-student');
|
||||||
|
} else {
|
||||||
|
auth()->login($userDatabase);
|
||||||
|
|
||||||
|
session()->regenerate();
|
||||||
|
return redirect()->route('dashboard-student');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected function createDatabase($dbUsername, $dbPassword) {
|
||||||
|
try {
|
||||||
|
$connection = DB::connection('mysql')->getPdo();
|
||||||
|
DB::statement("CREATE USER '{$dbUsername}'@'%' IDENTIFIED BY '';"); // Empty password
|
||||||
|
DB::statement("GRANT USAGE ON *.* TO '{$dbUsername}'@'%';");
|
||||||
|
DB::statement("ALTER USER '{$dbUsername}'@'%' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;");
|
||||||
|
|
||||||
|
// Create a database
|
||||||
|
DB::statement("CREATE DATABASE IF NOT EXISTS `{$dbUsername}`;");
|
||||||
|
DB::statement("GRANT ALL PRIVILEGES ON `{$dbUsername}`.* TO '{$dbUsername}'@'%';");
|
||||||
|
|
||||||
|
// Apply the changes immediately
|
||||||
|
DB::statement("FLUSH PRIVILEGES;");
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Handle exceptions such as permission issues or SQL errors
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
// Optionally, return an error message to the user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
iClOP-V2/app/Http/Controllers/Controller.php
Normal file
12
iClOP-V2/app/Http/Controllers/Controller.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
}
|
||||||
67
iClOP-V2/app/Http/Controllers/DataController.php
Normal file
67
iClOP-V2/app/Http/Controllers/DataController.php
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class DataController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$cards = [
|
||||||
|
[
|
||||||
|
'image' => './images/cards/Android.png',
|
||||||
|
'title' => 'Android programming with Java and Kotlin',
|
||||||
|
'topics' => '18 learning topics',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'image' => './images/cards/Flutter.png',
|
||||||
|
'title' => 'Mobile programming with Flutter',
|
||||||
|
'topics' => '18 learning topics',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'image' => './images/cards/Node.js.png',
|
||||||
|
'title' => 'Web application with Node.JS',
|
||||||
|
'topics' => '18 learning topics',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$cardsData = [
|
||||||
|
[
|
||||||
|
'image' => './images/cards/computer.png',
|
||||||
|
'title' => 'Fully Computer-Assisted Learning Platform',
|
||||||
|
'description' => 'Digital educational platform that utilizes artificial intelligence and machine learning to provide a comprehensive and interactive learning experience entirely driven by computer technology.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'image' => './images/cards/eos-icons_machine-learning.png',
|
||||||
|
'title' => 'Intelligence Guidance',
|
||||||
|
'description' => 'System or technology that provides automated support and guidance to learners, assisting them in their learning journey through intelligent algorithms and machine learning.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'image' => './images/cards/Grading.png',
|
||||||
|
'title' => 'Auto Grading',
|
||||||
|
'description' => 'Technology that automatically evaluates and scores assignments, exams, or assessments, eliminating the need for manual grading by instructors and providing efficient and consistent feedback to students.',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$cardsData2 = [
|
||||||
|
[
|
||||||
|
'image' => './images/cards/Intelligence.png',
|
||||||
|
'title' => 'Intelligence Learning Guidance',
|
||||||
|
'description' => 'Intelligence Learning Guidance utilizes AI and smart algorithms to provide personalized support, adapting to learners needs and optimizing their educational outcomes.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'image' => './images/cards/coding.png',
|
||||||
|
'title' => 'Practical Coding Approach',
|
||||||
|
'description' => 'Focuses on teaching coding skills through real-world examples, projects, and problem-solving scenarios, enabling learners to develop practical coding proficiency and problem-solving abilities.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'image' => './images/cards/Machine.png',
|
||||||
|
'title' => 'Online Virtual Machine',
|
||||||
|
'description' => 'Virtual computing environment accessible over the internet, enabling users to run applications, perform tasks, and store data without requiring physical hardware.',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('welcome', compact('cards', 'cardsData', 'cardsData2'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\NodeJS\Student;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\NodeJS\Project;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Yajra\DataTables\Facades\DataTables;
|
||||||
|
|
||||||
|
class DashboardController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$projects = Project::skip(0)->take(3)->get();
|
||||||
|
if ($request->ajax()) {
|
||||||
|
$data = DB::connection('nodejsDB')->table('projects')
|
||||||
|
->select('projects.id', 'projects.title', DB::raw('COUNT(submissions.id) as submission_count'))
|
||||||
|
->leftJoin('submissions', function ($join) use ($user) {
|
||||||
|
$join->on('projects.id', '=', 'submissions.project_id')
|
||||||
|
->where('submissions.user_id', '=', $user->id);
|
||||||
|
})
|
||||||
|
->groupBy('projects.id');
|
||||||
|
|
||||||
|
|
||||||
|
return Datatables::of($data)
|
||||||
|
->addIndexColumn()
|
||||||
|
->addColumn('title', function ($row) {
|
||||||
|
$title_button = '<a href="/nodejs/submissions/project/' . $row->id . '" class="underline text-secondary">' . $row->title . '</a>';
|
||||||
|
return $title_button;
|
||||||
|
})
|
||||||
|
->addColumn('submission_count', function ($row) {
|
||||||
|
$submission_count = $row->submission_count ?? 0;
|
||||||
|
$text = $submission_count > 0 ? 'Submitted' : 'No Submission';
|
||||||
|
$span_color = $submission_count > 0 ? 'green' : 'gray';
|
||||||
|
$span = '<span class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none bg-' . $span_color . '-100 text-' . $span_color . '-800">' . ucfirst($text) . '</span>';
|
||||||
|
return $span;
|
||||||
|
})
|
||||||
|
->rawColumns(['title', 'submission_count'])
|
||||||
|
->make(true);
|
||||||
|
}
|
||||||
|
return view('nodejs.dashboard.index', compact('projects'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\NodeJS\Student;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ExecutionStepController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user