1
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
src/.DS_Store
|
3
README.MD
Normal file
3
README.MD
Normal file
@@ -0,0 +1,3 @@
|
||||
# Welland Gouldsmith School Management System
|
||||
|
||||
This is the repository for my Welland Gouldsmith School Management System project made using the V Programming language, SQLite3 and HTMX
|
8
databases/config_databases_sqlite.v
Normal file
8
databases/config_databases_sqlite.v
Normal file
@@ -0,0 +1,8 @@
|
||||
module databases
|
||||
|
||||
import db.sqlite // can change to 'db.mysql', 'db.pg'
|
||||
|
||||
pub fn create_db_connection() !sqlite.DB {
|
||||
mut db := sqlite.connect('app.db')!
|
||||
return db
|
||||
}
|
39
src/faculty_controller.v
Normal file
39
src/faculty_controller.v
Normal file
@@ -0,0 +1,39 @@
|
||||
module main
|
||||
|
||||
import vweb
|
||||
|
||||
|
||||
@['/controller/faculty'; post]
|
||||
pub fn (mut app App) controller_get_faculty(email string, password string) vweb.Result {
|
||||
response := app.service_get_faculty(email, password) or {
|
||||
app.set_status(400, '')
|
||||
return app.text('${err}')
|
||||
}
|
||||
return app.json(response)
|
||||
}
|
||||
|
||||
@['/controller/faculty/create'; post]
|
||||
pub fn (mut app App) controller_create_faculty(firstname string, lastname string, email string, password string) vweb.Result {
|
||||
if firstname == '' {
|
||||
app.set_status(400, '')
|
||||
return app.text('firstname cannot be empty')
|
||||
}
|
||||
if lastname == '' {
|
||||
app.set_status(400, '')
|
||||
return app.text('lastname cannot be empty')
|
||||
}
|
||||
if email == '' {
|
||||
app.set_status(400, '')
|
||||
return app.text('username cannot be empty')
|
||||
}
|
||||
if password == '' {
|
||||
app.set_status(400, '')
|
||||
return app.text('password cannot be empty')
|
||||
}
|
||||
app.service_add_faculty(firstname, lastname, email, password) or {
|
||||
app.set_status(400, '')
|
||||
return app.text('error: ${err}')
|
||||
}
|
||||
app.set_status(201, '')
|
||||
return app.redirect('/faculty')
|
||||
}
|
13
src/faculty_entities.v
Normal file
13
src/faculty_entities.v
Normal file
@@ -0,0 +1,13 @@
|
||||
module main
|
||||
|
||||
@[table: 'faculty']
|
||||
pub struct Faculty {
|
||||
mut:
|
||||
id int @[primary; sql: serial]
|
||||
firstname string @[sql_type: 'TEXT']
|
||||
lastname string @[sql_type: 'TEXT']
|
||||
email string @[sql_type: 'TEXT'; unique]
|
||||
access_level int @[sql_type: 'INTEGER']
|
||||
password string @[sql_type: 'TEXT']
|
||||
// products []Product @[fkey: 'user_id']
|
||||
}
|
1
src/faculty_htmx.v
Normal file
1
src/faculty_htmx.v
Normal file
@@ -0,0 +1 @@
|
||||
module main
|
44
src/faculty_service.v
Normal file
44
src/faculty_service.v
Normal file
@@ -0,0 +1,44 @@
|
||||
module main
|
||||
|
||||
import databases
|
||||
|
||||
// service_add_faculty is a function that adds a faculty member to the database.
|
||||
fn (mut app App) service_add_faculty(firstname string, lastname string, email string, password string) ! {
|
||||
mut db := databases.create_db_connection()!
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
faculty_model := Faculty{
|
||||
firstname: firstname
|
||||
lastname: lastname
|
||||
email: email
|
||||
password: password
|
||||
}
|
||||
|
||||
mut insert_error := ''
|
||||
sql db {
|
||||
insert faculty_model into Faculty
|
||||
} or { insert_error = err.msg() }
|
||||
if insert_error != '' {
|
||||
return error(insert_error)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) service_get_faculty(email string, password string) !Faculty {
|
||||
mut db := databases.create_db_connection() or {
|
||||
println(err)
|
||||
return err
|
||||
}
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
results := sql db {
|
||||
select from Faculty where email == email && password == password
|
||||
}!
|
||||
if results.len == 0 {
|
||||
return error('no results')
|
||||
}
|
||||
return results[0]
|
||||
}
|
33
src/faculty_view.v
Normal file
33
src/faculty_view.v
Normal file
@@ -0,0 +1,33 @@
|
||||
module main
|
||||
|
||||
import vweb
|
||||
|
||||
@['/faculty/login'; get]
|
||||
pub fn (mut app App) faculty_login() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
@['/faculty'; get]
|
||||
pub fn (mut app App) faculty_admin() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
@['/faculty/schedule'; get]
|
||||
pub fn (mut app App) faculty_schedule() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
@['/faculty/attendance'; get]
|
||||
pub fn (mut app App) faculty_attendance() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
||||
|
||||
@['/faculty/signup'; get]
|
||||
pub fn (mut app App) faculty_signup() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
21
src/index.html
Normal file
21
src/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>School Management System</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Welcome to Welland Gouldsmith Management System</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section class="options">
|
||||
<h2>Choose an option:</h2>
|
||||
<a href="/student">Check Attendance (Student)</a>
|
||||
<a href="/faculty/login">Faculty Login</a>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
40
src/main.v
Normal file
40
src/main.v
Normal file
@@ -0,0 +1,40 @@
|
||||
module main
|
||||
|
||||
import vweb
|
||||
import databases
|
||||
import os
|
||||
|
||||
const (
|
||||
port = 8082
|
||||
)
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
}
|
||||
|
||||
pub fn (app App) before_request() {
|
||||
println('[web] before_request: ${app.req.method} ${app.req.url}')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut db := databases.create_db_connection() or { panic(err) }
|
||||
|
||||
sql db {
|
||||
create table Faculty
|
||||
// create table Product
|
||||
} or { panic('error on create table: ${err}') }
|
||||
|
||||
db.close() or { panic(err) }
|
||||
|
||||
mut app := &App{}
|
||||
app.serve_static('/favicon.ico', 'src/assets/favicon.ico')
|
||||
// makes all static files available.
|
||||
app.mount_static_folder_at(os.resource_abs_path('.'), '/')
|
||||
|
||||
vweb.run(app, port)
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
0
src/principle_controller.v
Normal file
0
src/principle_controller.v
Normal file
0
src/principle_service.v
Normal file
0
src/principle_service.v
Normal file
0
src/principle_view.v
Normal file
0
src/principle_view.v
Normal file
11
src/students_entities.v
Normal file
11
src/students_entities.v
Normal file
@@ -0,0 +1,11 @@
|
||||
module main
|
||||
|
||||
@[table: 'students']
|
||||
pub struct Student {
|
||||
mut:
|
||||
id int @[primary; sql: serial]
|
||||
firstname string @[sql_type: 'TEXT']
|
||||
lastname string @[sql_type: 'TEXT']
|
||||
email string @[sql_type: 'TEXT'; unique]
|
||||
password string @[sql_type: 'TEXT']
|
||||
}
|
0
src/students_service.v
Normal file
0
src/students_service.v
Normal file
9
src/students_view.v
Normal file
9
src/students_view.v
Normal file
@@ -0,0 +1,9 @@
|
||||
module main
|
||||
|
||||
import vweb
|
||||
|
||||
@['/student'; get]
|
||||
pub fn (mut app App) student_view() vweb.Result {
|
||||
|
||||
return $vweb.html()
|
||||
}
|
27
src/templates/faculty/admin.html
Normal file
27
src/templates/faculty/admin.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Faculty Admin</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Faculty Admin Panel</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section class="admin-options">
|
||||
<h2>Welcome, [Faculty Name]!</h2>
|
||||
<ul>
|
||||
<li><a href="/faculty/schedule">View Class Schedule</a></li>
|
||||
<li><a href="/faculty/attendance">Take Attendance</a></li>
|
||||
<li><a href="#">Record Grades</a></li>
|
||||
<li><a href="#">View Student Information</a></li>
|
||||
<li><a href="#">Send Announcements</a></li>
|
||||
<li><a href="#">Logout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
36
src/templates/faculty/attendance.html
Normal file
36
src/templates/faculty/attendance.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Take Attendance</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Faculty Admin Panel</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section class="admin-content">
|
||||
<h2>Take Attendance (Select Class)</h2>
|
||||
<form action="#">
|
||||
<select id="course_select">
|
||||
<option value="">Select a Course</option>
|
||||
<option value="math">Math (Monday, 9:00 AM)</option>
|
||||
<option value="english">English (Tuesday, 10:30 AM)</option>
|
||||
<option value="science">Science (Wednesday, 1:00 PM)</option>
|
||||
</select>
|
||||
<button type="submit">Take Attendance</button>
|
||||
</form>
|
||||
<p id="attendance_message"></p>
|
||||
<script>
|
||||
document.getElementById('course_select').addEventListener('change', function() {
|
||||
const messageElement = document.getElementById('attendance_message');
|
||||
messageElement.textContent = 'Select a course and click "Take Attendance" to proceed. (Attendance functionality not implemented in this demo)';
|
||||
});
|
||||
</script>
|
||||
<a href="/faculty">Back to Admin Panel</a>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
24
src/templates/faculty/login.html
Normal file
24
src/templates/faculty/login.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Faculty Login</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Faculty Login</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section class="login">
|
||||
<form action="#"> <label for="email">Email:</label>
|
||||
<input name="email" type="email" id="username" required>
|
||||
<label for="password">Password:</label>
|
||||
<input name="password" type="password" id="password" required>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
25
src/templates/faculty/schedule.html
Normal file
25
src/templates/faculty/schedule.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>View Class Schedule</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Faculty Admin Panel</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section class="admin-content">
|
||||
<h2>Your Class Schedule</h2>
|
||||
<p>**Day** | **Time** | **Course** | **Location**</p>
|
||||
<p>----- | -------- | -------- | --------</p>
|
||||
<p>Monday | 9:00 AM - 10:00 AM | Math | Room 201</p>
|
||||
<p>Tuesday | 10:30 AM - 12:00 PM | English | Room 305</p>
|
||||
<p>Wednesday | 1:00 PM - 2:00 PM | Science | Lab 102</p>
|
||||
<a href="/faculty">Back to Admin Panel</a>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
29
src/templates/faculty/signup.html
Normal file
29
src/templates/faculty/signup.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Faculty Login</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Faculty Login</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section class="login">
|
||||
<form action="/controller/faculty/create" method="post">
|
||||
<label for="firstname">First Name:</label>
|
||||
<input name="firstname" type="text" id="firstname" required>
|
||||
<label for="lastname">Last Name:</label>
|
||||
<input name="lastname" type="text" id="lastname" required>
|
||||
<label for="email">Email:</label>
|
||||
<input name="email" type="email" id="email" required>
|
||||
<label for="password">Password:</label>
|
||||
<input name="password" type="password" id="password" required>
|
||||
<button type="submit">Sign Up</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
15
src/templates/header_component.html
Normal file
15
src/templates/header_component.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<nav>
|
||||
<div class='nav-wrapper'>
|
||||
<a href='javascript:window.history.back();' class='left'>
|
||||
<i class='material-icons'>arrow_back_ios_new</i>
|
||||
</a>
|
||||
<a href='/'>
|
||||
<img src='src/assets/veasel.png' alt='logo' style='max-height: 100%' />
|
||||
</a>
|
||||
<ul id='nav-mobile' class='right'>
|
||||
<li><a href='https://github.com/vlang/v'>github</a></li>
|
||||
<li><a href='https://vlang.io/'>website</a></li>
|
||||
<li><a href='https://github.com/sponsors/medvednikov'>support</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
11
src/templates/products.css
Normal file
11
src/templates/products.css
Normal file
@@ -0,0 +1,11 @@
|
||||
h1.title {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #3b7bbf;
|
||||
}
|
||||
|
||||
div.products-table {
|
||||
border: 1px solid;
|
||||
max-width: 720px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
94
src/templates/products.html
Normal file
94
src/templates/products.html
Normal file
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--Let browser know website is optimized for mobile-->
|
||||
<meta charset='UTF-8' name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||
|
||||
<!-- Compiled and minified CSS -->
|
||||
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css'>
|
||||
|
||||
<!-- Compiled and minified JavaScript -->
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js'></script>
|
||||
|
||||
<!-- Material UI icons -->
|
||||
<link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet'>
|
||||
|
||||
<title>Login</title>
|
||||
@css 'src/templates/products.css'
|
||||
</head>
|
||||
<body>
|
||||
<div>@include 'header_component.html'</div>
|
||||
<h1 class='title'>Hi, ${user.username}! you are online</h1>
|
||||
<!-- <button onclick='document.location.reload(true)'>Lala</button> -->
|
||||
<form id='product_form' method='post' action=''>
|
||||
<div class='row'>
|
||||
<div class='input-field col s2'>
|
||||
<input id='product_name' name='product_name' type='text' class='validate'>
|
||||
<label class='active' for='product_name'>product name</label>
|
||||
</div>
|
||||
<div style='margin-top: 10px;'>
|
||||
<input class='waves-effect waves-light btn-small' type='submit' onclick='addProduct()' formaction='javascript:void(0);' value='Register' required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div style='width: 20; height: 300;'>
|
||||
<input type='text' name='product_name' placeholder='product name' required autofocus>
|
||||
</div> -->
|
||||
</form>
|
||||
<script type='text/javascript'>
|
||||
function getCookie(cookieName) {
|
||||
let cookie = {};
|
||||
document.cookie.split(';').forEach(function(el) {
|
||||
let [key,value] = el.split('=');
|
||||
cookie[key.trim()] = value;
|
||||
})
|
||||
return cookie[cookieName];
|
||||
}
|
||||
async function addProduct() {
|
||||
const form = document.querySelector('#product_form');
|
||||
const formData = new FormData(form);
|
||||
console.log(getCookie('token'));
|
||||
await fetch('/controller/product/create', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers :{
|
||||
token: getCookie('token')
|
||||
}
|
||||
})
|
||||
.then( async (response) => {
|
||||
if (response.status != 201) {
|
||||
throw await response.text()
|
||||
}
|
||||
return await response.text()
|
||||
})
|
||||
.then((data) => {
|
||||
// alert('User created successfully')
|
||||
document.location.reload(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<div class='products-table card-panel'>
|
||||
<table class='highlight striped responsive-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Created date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@for product in user.products
|
||||
<tr>
|
||||
<td>${product.id}</td>
|
||||
<td>${product.name}</td>
|
||||
<td>${product.created_at}</td>
|
||||
</tr>
|
||||
@end
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
6
src/templates/student/student.js
Normal file
6
src/templates/student/student.js
Normal file
@@ -0,0 +1,6 @@
|
||||
function checkAttendance() {
|
||||
// Simulate attendance check (replace with actual logic)
|
||||
const studentId = document.getElementById('student_id').value;
|
||||
const attendanceMessage = document.getElementById('attendance_message');
|
||||
attendanceMessage.textContent = `Attendance data for student ID ${studentId} is not available in this demo.`;
|
||||
}
|
23
src/templates/student/view.html
Normal file
23
src/templates/student/view.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Student Attendance</title>
|
||||
@css '/templates/style.css'
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Check Attendance</h1>
|
||||
</header>
|
||||
<main>
|
||||
<section>
|
||||
<p>Enter your Student ID:</p>
|
||||
<input type="text" id="student_id">
|
||||
<button onclick="checkAttendance()">Check</button>
|
||||
<p id="attendance_message"></p>
|
||||
</section>
|
||||
</main>
|
||||
@js '/templates/student/attendance.js'
|
||||
</body>
|
||||
</html>
|
33
src/templates/style.css
Normal file
33
src/templates/style.css
Normal file
@@ -0,0 +1,33 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: calc(100vh - 120px); /* Subtract header and footer height */
|
||||
}
|
||||
|
||||
.options {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.options a {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #ddd;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.options a:hover {
|
||||
background-color: #eee;
|
||||
}
|
Reference in New Issue
Block a user