create: new folder

This commit is contained in:
abiyasa05 2024-12-31 11:25:09 +07:00
parent 3d7f92c177
commit 3990ecc391
3639 changed files with 1240235 additions and 1 deletions

Binary file not shown.

BIN
Manual Program/RUII2.docx Normal file

Binary file not shown.

BIN
Manual Program/RUII3.docx Normal file

Binary file not shown.

BIN
Manual Program/RUII4.docx Normal file

Binary file not shown.

BIN
Manual Program/RUII5.docx Normal file

Binary file not shown.

BIN
Manual Program/RUII6.docx Normal file

Binary file not shown.

Binary file not shown.

BIN
Reactjs1/Modul/RUII1.docx Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII1.pdf Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII2.docx Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII2.pdf Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII3.docx Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII3.pdf Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII4.docx Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII4.pdf Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII5.docx Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII5.pdf Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII6.docx Normal file

Binary file not shown.

BIN
Reactjs1/Modul/RUII6.pdf Normal file

Binary file not shown.

23
Reactjs1/my-app/.gitignore vendored Normal file
View 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*

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

70
Reactjs1/my-app/README.md Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View 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;

View 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);
}
}

View 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;

View 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.');
}

View 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.');
}

View 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.');
}

View 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.');
}

View 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
}

View 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();
});
});

View 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!');
});
});

View 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();
});
});

View 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>
);
};

View 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>
);
};

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
};

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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;

View 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>
);
};

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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;

View 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;
}

View 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}>&divide;</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}>&times;</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}>&ndash;</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;

View 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;
}

View 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>&copy; 2024 Belajar Reactjs. All rights reserved.</p>
</div>
</footer>
);
};
export default Footer;

View 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;
}

View 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;

View 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;
}

View 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>
);
}

View 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;
}

View 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;

View 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>
);
}

View 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;
}

View 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;

View 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;
}
} */

View 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;
}

View 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();

View 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

View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

BIN
iCLOP_React.js_Komang.mp4 Normal file

Binary file not shown.

18
iClOP-V2/.editorconfig Normal file
View 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
View 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
View 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
View 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
View 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
View 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).

View 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');
}
}

View 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) {
//
});
}
}

View 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);
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\Android23;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AssignController extends Controller
{
//
}

View 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');
}
}
}

View 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'));
}
}

View 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);
}
}

View 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
}
}
}

View 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;
}

View 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'));
}
}

View File

@ -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'));
}
}

View File

@ -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