diff --git a/.gitignore b/.gitignore index 82f5e3c..b4f8834 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ edusync-7bd5e-firebase-adminsdk-x49uh-af084a6314.json edusync-test-firebase-adminsdk-hk5kl-9af0162b09.json .env -*.json +config.json +edusync-426009-343696fa49b1.json *.exe \ No newline at end of file diff --git a/EduSync.exe b/EduSync.exe index bf099b4..b876bef 100644 Binary files a/EduSync.exe and b/EduSync.exe differ diff --git a/adminHandler.go b/adminHandler.go index 1f18bb7..bb8d5ae 100644 --- a/adminHandler.go +++ b/adminHandler.go @@ -31,6 +31,24 @@ func AdminHandler(router *mux.Router) { t.Execute(res, nil) }).Methods("GET") + router.HandleFunc("/admin/profile", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/admin/profile.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, nil) + }).Methods("GET") + + router.HandleFunc("/admin/profile", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/admin/profile.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, nil) + }).Methods("GET") + //searve the search student page router.HandleFunc("/admin/search_student", func(res http.ResponseWriter, req *http.Request) { t, err := template.ParseFiles("templates/admin/search_student.html") @@ -428,7 +446,7 @@ func AdminHandler(router *mux.Router) { }).Methods("GET", "PUT") // Create a new announcement - router.HandleFunc("/admin/announcement/", func(res http.ResponseWriter, req *http.Request) { + router.HandleFunc("/admin/announcement", func(res http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodPost: var announcement Announcement @@ -454,4 +472,33 @@ func AdminHandler(router *mux.Router) { // POST /admin/announcement // Request Body: JSON object with announcement details // Response: HTTP Status Created (201) + + router.HandleFunc("/admin/api/profile", func(res http.ResponseWriter, req *http.Request) { + currentUser, err := GetCurrentUser(req) + if err != nil { + http.Error(res, "Unauthorized", http.StatusUnauthorized) + return + } + switch req.Method { + case http.MethodGet: + admin, err := readAdmin(currentUser.GoogleID, req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + res.Header().Set("Content-Type", "application/json") + json.NewEncoder(res).Encode(admin) + case http.MethodPut: + var updates map[string]interface{} + if err := json.NewDecoder(req.Body).Decode(&updates); err != nil { + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + if err := updateAdmin(currentUser.GoogleID, updates, req); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + res.WriteHeader(http.StatusNoContent) + } + }).Methods("GET", "PUT") } diff --git a/authHandler.go b/authHandler.go index 48a08f5..9e004de 100644 --- a/authHandler.go +++ b/authHandler.go @@ -15,8 +15,8 @@ import ( ) func AuthHandler(router *mux.Router, config *Config) { - maxAge := 86400 * 30 // 30 days - isProd := true // Set to true when serving over https + maxAge := 3600 // 1 hour + isProd := true // Set to true when serving over https store = sessions.NewCookieStore( []byte(config.AuthKey), @@ -26,16 +26,23 @@ func AuthHandler(router *mux.Router, config *Config) { store.Options.Path = "/" store.Options.HttpOnly = true // HttpOnly should always be enabled store.Options.Secure = isProd - store.Options = &sessions.Options{ - Path: "/", - MaxAge: 3600, // 1 hour - HttpOnly: true, - Secure: true, // This should be true if your application is served over HTTPS - } gothic.Store = store goth.UseProviders(google.New(config.GoogleClientID, config.GoogleClientSecret, "https://localhost:8080/auth/google/callback", "email", "profile", "https://www.googleapis.com/auth/drive.file")) + router.HandleFunc("/login", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/login.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, false) + }).Methods("GET") + + router.HandleFunc("/auth/{provider}", func(res http.ResponseWriter, req *http.Request) { + gothic.BeginAuthHandler(res, req) + }).Methods("GET") + router.HandleFunc("/auth/{provider}/callback", func(res http.ResponseWriter, req *http.Request) { user, err := gothic.CompleteUserAuth(res, req) if err != nil { @@ -74,17 +81,22 @@ func AuthHandler(router *mux.Router, config *Config) { } }).Methods("GET") - router.HandleFunc("/auth/{provider}", func(res http.ResponseWriter, req *http.Request) { - gothic.BeginAuthHandler(res, req) - }).Methods("GET") - - router.HandleFunc("/login", func(res http.ResponseWriter, req *http.Request) { - t, err := template.ParseFiles("templates/login.html") - if err != nil { - http.Error(res, err.Error(), http.StatusInternalServerError) - return - } - t.Execute(res, false) + router.HandleFunc("/logout", func(res http.ResponseWriter, req *http.Request) { + // Clear the session or cookie + http.SetCookie(res, &http.Cookie{ + Name: "session_token", + Value: "", + Path: "/", + MaxAge: -1, // This will delete the cookie + }) + + // Set headers to prevent caching + res.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0") + res.Header().Set("Cache-Control", "post-check=0, pre-check=0") + res.Header().Set("Pragma", "no-cache") + + // Redirect to the login page or home page + http.Redirect(res, req, "/", http.StatusFound) // 302 Found }).Methods("GET") } diff --git a/config.go b/config.go index 71c42ff..d31c118 100644 --- a/config.go +++ b/config.go @@ -10,7 +10,6 @@ import ( type Config struct { GoogleClientID string `json:"google_client_id"` GoogleClientSecret string `json:"google_client_secret"` - SessionSecret string `json:"session_secret"` AuthKey string `json:"auth_key"` EncryptKey string `json:"encrypt_key"` } diff --git a/database.go b/database.go index 2abf6b0..beda59b 100644 --- a/database.go +++ b/database.go @@ -30,7 +30,7 @@ func SessionCookie() (string, error) { return sessionCookieStore, nil } -var sessionCookieStore, err = SessionCookie() +var sessionCookieStore, _ = SessionCookie() var store = sessions.NewCookieStore([]byte(sessionCookieStore)) func initDB(app *firebase.App) error { @@ -43,6 +43,48 @@ func initDB(app *firebase.App) error { return nil } +func getUserRole(email string) (User, string, error) { + ctx := context.Background() + var user User + var userRole string + + // Check if firebaseClient is initialized + if firebaseClient == nil { + log.Println("Firebase client is not initialized") + return user, userRole, fmt.Errorf("firebase client is not initialized") + } + + // Map categories to Firebase references + categoryRefs := map[string]string{ + "Student": "/students", + "Parent": "/parents", + "Instructor": "/instructors", + "Admin": "/admins", + } + + // Iterate through each category and check if the email exists + for category, ref := range categoryRefs { + categoryRef := firebaseClient.NewRef(ref) + dataSnapshot, err := categoryRef.OrderByChild("email").EqualTo(email).GetOrdered(ctx) + if err != nil { + log.Printf("Error fetching data from %s: %v", category, err) + continue + } + + // Check if dataSnapshot has any children + if len(dataSnapshot) > 0 { + userRole = category + // Assuming dataSnapshot[0] is the first match and it contains the user data + if err := dataSnapshot[0].Unmarshal(&user); err != nil { + log.Printf("Error unmarshalling data for %s: %v", category, err) + continue + } + break + } + } + return user, userRole, nil +} + // Utility function to get current user func GetCurrentUser(req *http.Request) (User, error) { session, err := store.Get(req, "auth-session") @@ -64,6 +106,295 @@ func GetCurrentUser(req *http.Request) (User, error) { return user, nil } +// Utility function to get current instructor +func GetCurrentInstructor(req *http.Request) (Instructor, error) { + user, err := GetCurrentUser(req) + if err != nil { + return Instructor{}, err + } + + if user.Role != "Instructor" { + return Instructor{}, fmt.Errorf("current user is not an instructor") + } + + // Query Firebase to find the instructor object with the same email as the user + ref := firebaseClient.NewRef("instructors") + var instructorsMap map[string]Instructor + if err := ref.Get(context.TODO(), &instructorsMap); err != nil { + return Instructor{}, fmt.Errorf("error reading instructors: %v", err) + } + + // Find the instructor with the same email as the user + var instructor Instructor + found := false + for _, i := range instructorsMap { + if i.Email == user.Email { + instructor = i + found = true + break + } + } + if !found { + return Instructor{}, fmt.Errorf("instructor not found for the current user") + } + return instructor, nil +} + +// Handler to get classes for the current instructor +func GetInstructorClasses(res http.ResponseWriter, req *http.Request) { + instructor, err := GetCurrentInstructor(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + // Query Firebase to get classes for the instructor + ref := firebaseClient.NewRef("classes") + var classesMap map[string]Class + if err := ref.Get(context.TODO(), &classesMap); err != nil { + http.Error(res, fmt.Sprintf("error reading classes: %v", err), http.StatusInternalServerError) + return + } + + var cp, dn, ie, py, sc, te bool + cp, dn, ie, py, sc, te = false, false, false, false, false, false + + // Filter classes by instructor's email + var instructorClasses [][]string + for _, class := range classesMap { + if class.Instructor == instructor.Email && class.Name == "CP" && !cp { + instructorClasses = append(instructorClasses, []string{"Coding Pioneers", class.FolderID}) + cp = true + } else if class.Instructor == instructor.Email && class.Name == "DN" && !dn { + instructorClasses = append(instructorClasses, []string{"Digital Navigators", class.FolderID}) + dn = true + } else if class.Instructor == instructor.Email && class.Name == "IE" && !ie { + instructorClasses = append(instructorClasses, []string{"Innovation Engineers", class.FolderID}) + ie = true + } else if class.Instructor == instructor.Email && class.Name == "PY" && !py { + instructorClasses = append(instructorClasses, []string{"Python", class.FolderID}) + py = true + } else if class.Instructor == instructor.Email && class.Name == "SC" && !sc { + instructorClasses = append(instructorClasses, []string{"Scratch", class.FolderID}) + sc = true + } else if class.Instructor == instructor.Email && class.Name == "TE" && !te { + instructorClasses = append(instructorClasses, []string{"Tech Explorers", class.FolderID}) + te = true + } + } + + // Return the instructor's classes as JSON + res.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(res).Encode(instructorClasses); err != nil { + http.Error(res, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) + } +} + +// Handler to get classes for the current instructor +func GetInstructorClassIds(res http.ResponseWriter, req *http.Request) { + instructor, err := GetCurrentInstructor(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + // Query Firebase to get classes for the instructor + ref := firebaseClient.NewRef("classes") + var classesMap map[string]Class + if err := ref.Get(context.TODO(), &classesMap); err != nil { + http.Error(res, fmt.Sprintf("error reading classes: %v", err), http.StatusInternalServerError) + return + } + // Filter classes by instructor's email + var instructorClasses []string + for _, class := range classesMap { + if class.Instructor == instructor.Email { + instructorClasses = append(instructorClasses, class.ClassID) + } + } + + // Return the instructor's classes as JSON + res.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(res).Encode(instructorClasses); err != nil { + http.Error(res, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) + } +} + +// Function to get students by ClassID and their corresponding Parent FolderIDs +func GetStudentsAndFoldersByClassID(classID string, res http.ResponseWriter, req *http.Request) { + // Query Firebase to get students + ref := firebaseClient.NewRef("students") + var studentsMap map[string]Student + if err := ref.Get(context.TODO(), &studentsMap); err != nil { + http.Error(res, fmt.Sprintf("error reading students: %v", err), http.StatusInternalServerError) + return + } + + // Query Firebase to get parents + refParents := firebaseClient.NewRef("parents") + var parentsMap map[string]Parent + if err := refParents.Get(context.TODO(), &parentsMap); err != nil { + http.Error(res, fmt.Sprintf("error reading parents: %v", err), http.StatusInternalServerError) + return + } + + // Prepare the result + var result []map[string]string + for _, student := range studentsMap { + if student.ClassID == classID { + parent, ok := parentsMap[student.ParentID] + if !ok { + http.Error(res, fmt.Sprintf("parent not found for student %s", student.Name), http.StatusNotFound) + return + } + result = append(result, map[string]string{ + "name": student.Name, + "folderID": parent.FolderID, + }) + } + } + + // Return the result as JSON + res.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(res).Encode(result); err != nil { + http.Error(res, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) + } +} + +// Utility function to get current student +func GetCurrentStudent(req *http.Request) (Student, error) { + user, err := GetCurrentUser(req) + if err != nil { + return Student{}, err + } + + if user.Role != "Student" { + return Student{}, fmt.Errorf("current user is not a student") + } + + // Query Firebase to find the student object with the same email as the user + ref := firebaseClient.NewRef("students") + var studentsMap map[string]Student + if err := ref.Get(context.TODO(), &studentsMap); err != nil { + return Student{}, fmt.Errorf("error reading students: %v", err) + } + + // Find the student with the same email as the user + var student Student + found := false + for _, s := range studentsMap { + if s.Email == user.Email { + student = s + found = true + break + } + } + if !found { + return Student{}, fmt.Errorf("student not found for the current user") + } + return student, nil +} + +// Function to get the folder ID of a student's class +func GetStudentFolder(req *http.Request) (string, error) { + student, err := GetCurrentStudent(req) + if err != nil { + return "", err + } + + classID := student.ClassID + if classID == "" { + return "", fmt.Errorf("student is not enrolled in any class") + } + + // Fetch class information based on ClassID + class, err := GetClassByID(classID) + if err != nil { + return "", err + } + return class.FolderID, nil +} + +// Function to fetch a class by its ID from Firebase +func GetClassByID(classID string) (Class, error) { + ref := firebaseClient.NewRef("classes/" + classID) + + var class Class + if err := ref.Get(context.TODO(), &class); err != nil { + return Class{}, fmt.Errorf("error reading class: %v", err) + } + + return class, nil +} + +// Utility function to get current parent +func GetCurrentParent(req *http.Request) (Parent, error) { + user, err := GetCurrentUser(req) + if err != nil { + return Parent{}, err + } + + if user.Role != "Parent" { + return Parent{}, fmt.Errorf("current user is not a parent") + } + + // Query Firebase to find the parent object with the same email as the user + ref := firebaseClient.NewRef("parents") + var parentsMap map[string]Parent + if err := ref.Get(context.TODO(), &parentsMap); err != nil { + return Parent{}, fmt.Errorf("error reading parents: %v", err) + } + + // Find the parent with the same email as the user + var parent Parent + found := false + for _, p := range parentsMap { + if p.Email == user.Email { + parent = p + found = true + break + } + } + if !found { + return Parent{}, fmt.Errorf("parent not found for the current user") + } + return parent, nil +} + +// Utility function to get current admin +func GetCurrentAdmin(req *http.Request) (Admin, error) { + user, err := GetCurrentUser(req) + if err != nil { + return Admin{}, err + } + + if user.Role != "Admin" { + return Admin{}, fmt.Errorf("current user is not an admin") + } + + // Query Firebase to find the admin object with the same email as the user + ref := firebaseClient.NewRef("admins") + var adminsMap map[string]Admin + if err := ref.Get(context.TODO(), &adminsMap); err != nil { + return Admin{}, fmt.Errorf("error reading admins: %v", err) + } + + // Find the admin with the same email as the user + var admin Admin + found := false + for _, a := range adminsMap { + if a.Email == user.Email { + admin = a + found = true + break + } + } + if !found { + return Admin{}, fmt.Errorf("admin not found for the current user") + } + return admin, nil +} + // Utility functions to check roles func isAdmin(user User) bool { return user.Role == "Admin" @@ -128,48 +459,6 @@ func isParentChildInClass(user User, students []Student, class Class) bool { return false } -func getUserRole(email string) (User, string, error) { - ctx := context.Background() - var user User - var userRole string - - // Check if firebaseClient is initialized - if firebaseClient == nil { - log.Println("Firebase client is not initialized") - return user, userRole, fmt.Errorf("firebase client is not initialized") - } - - // Map categories to Firebase references - categoryRefs := map[string]string{ - "Student": "/students", - "Parent": "/parents", - "Instructor": "/instructors", - "Admin": "/admins", - } - - // Iterate through each category and check if the email exists - for category, ref := range categoryRefs { - categoryRef := firebaseClient.NewRef(ref) - dataSnapshot, err := categoryRef.OrderByChild("email").EqualTo(email).GetOrdered(ctx) - if err != nil { - log.Printf("Error fetching data from %s: %v", category, err) - continue - } - - // Check if dataSnapshot has any children - if len(dataSnapshot) > 0 { - userRole = category - // Assuming dataSnapshot[0] is the first match and it contains the user data - if err := dataSnapshot[0].Unmarshal(&user); err != nil { - log.Printf("Error unmarshalling data for %s: %v", category, err) - continue - } - break - } - } - return user, userRole, nil -} - // CRUD operations with role checks // Student CRUD @@ -241,7 +530,9 @@ func searchStudents(name, class string) ([]Student, error) { } var filteredStudents []Student for _, student := range students { - if name == "" || strings.Contains(strings.ToLower(student.Name), strings.ToLower(name)) { + // Check both name and class + if (name == "" || strings.Contains(strings.ToLower(student.Name), strings.ToLower(name))) && + (class == "" || strings.Contains(strings.ToLower(student.ClassID), strings.ToLower(class))) { filteredStudents = append(filteredStudents, student) } } diff --git a/database_model.go b/database_model.go index 99dd27c..fa47f7a 100644 --- a/database_model.go +++ b/database_model.go @@ -24,8 +24,6 @@ type Student struct { LessonCredits float32 `json:"lesson_credits"` ClassID string `json:"class_id"` ParentID string `json:"parent_id"` - // Instructor string `json:"instructor"` - // ParentName string `json:"parent_name"` } // Instructor struct for storing instructor information @@ -44,10 +42,12 @@ type Admin struct { // Parent struct for storing parent information type Parent struct { User + FolderID string `json:"folder_id"` } type Class struct { ClassID string `json:"class_id"` + FolderID string `json:"folder_id"` Name string `json:"class_name"` Instructor string `json:"instructor"` Duration float64 `json:"duration"` diff --git a/database_test.go b/database_test.go index 0329564..b5a90a0 100644 --- a/database_test.go +++ b/database_test.go @@ -77,12 +77,15 @@ var ( Email: "janedoe_parent@nk.com", Role: "Parent", }, + FolderID: "1F38DgyV0V5q2CWzoO3xkHTC4FY6cNrig", } classes = []Class{ { - ClassID: "te-6-10", - Name: "Test Class", + ClassID: "te-6-10", + Name: "Test Class", + Instructor: "jeyvianang112462@gmail.com", + FolderID: "1zrj8iUefB9D0u1RXT8Pi0-At8aQf9n2m", }, } diff --git a/drive.go b/drive.go new file mode 100644 index 0000000..6ebbd41 --- /dev/null +++ b/drive.go @@ -0,0 +1,173 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + "github.com/gorilla/mux" + "golang.org/x/oauth2/google" + "google.golang.org/api/drive/v3" + "google.golang.org/api/option" +) + +const ServiceAccountCredentials = "edusync-426009-343696fa49b1.json" + +func DriveHandler(router *mux.Router) { + router.HandleFunc("/api/files", func(res http.ResponseWriter, req *http.Request) { + folderID := req.URL.Query().Get("folder_id") + srv, err := createDriveService() + if err != nil { + http.Error(res, "Unable to create Drive service", http.StatusInternalServerError) + return + } + + files, err := listFiles(srv, folderID) + if err != nil { + http.Error(res, "Unable to retrieve files", http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "application/json") + json.NewEncoder(res).Encode(files) + }).Methods("GET") + + router.HandleFunc("/api/upload", func(res http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + http.Error(res, "Invalid request method", http.StatusMethodNotAllowed) + return + } + + folderID := req.FormValue("folderId") + if folderID == "" { + http.Error(res, "folderId is required", http.StatusBadRequest) + return + } + + fileName := req.FormValue("fileName") + if fileName == "" { + http.Error(res, "fileName is required", http.StatusBadRequest) + return + } + + file, _, err := req.FormFile("file") + if err != nil { + http.Error(res, err.Error(), http.StatusBadRequest) + return + } + defer file.Close() + + srv, err := createDriveService() + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + uploadedFile, err := uploadFileToDrive(srv, folderID, file, fileName) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "application/json") + json.NewEncoder(res).Encode(uploadedFile) + }).Methods("POST") + + router.HandleFunc("/api/media-upload", func(res http.ResponseWriter, req *http.Request) { + // Initialize Google Drive client + srv, err := createDriveService() + if err != nil { + http.Error(res, "Failed to initialize Drive client", http.StatusInternalServerError) + return + } + + err = req.ParseMultipartForm(10 << 20) // 10 MB limit + if err != nil { + http.Error(res, "Failed to parse form", http.StatusBadRequest) + return + } + + files := req.MultipartForm.File["files"] + folderIds := req.MultipartForm.Value["folderIds"] + studentNames := req.MultipartForm.Value["studentNames"] + + if len(files) != len(folderIds) || len(folderIds) != len(studentNames) { + http.Error(res, "Mismatch between files, folder IDs, and student names", http.StatusBadRequest) + return + } + + var results []string + + for i, fileHeader := range files { + file, err := fileHeader.Open() + if err != nil { + http.Error(res, fmt.Sprintf("Failed to open file: %v", err), http.StatusInternalServerError) + return + } + defer file.Close() + + folderID := folderIds[i] + studentName := studentNames[i] + + _, err = uploadFileToDrive(srv, folderID, file, fileHeader.Filename) + if err != nil { + results = append(results, fmt.Sprintf("Failed to upload %s for %s: %v", fileHeader.Filename, studentName, err)) + } else { + results = append(results, fmt.Sprintf("Successfully uploaded %s for %s", fileHeader.Filename, studentName)) + } + } + + res.Header().Set("Content-Type", "application/json") + json.NewEncoder(res).Encode(results) + }).Methods("POST") +} + +func createDriveService() (*drive.Service, error) { + ctx := context.Background() + + b, err := os.ReadFile(ServiceAccountCredentials) + if err != nil { + return nil, fmt.Errorf("unable to read client secret file: %v", err) + } + + config, err := google.JWTConfigFromJSON(b, drive.DriveFileScope) + if err != nil { + return nil, fmt.Errorf("unable to parse client secret file to config: %v", err) + } + + client := config.Client(ctx) + + srv, err := drive.NewService(ctx, option.WithHTTPClient(client)) + if err != nil { + return nil, fmt.Errorf("unable to retrieve Drive client: %v", err) + } + + return srv, nil +} + +func listFiles(service *drive.Service, folderID string) ([]*drive.File, error) { + query := fmt.Sprintf("'%s' in parents", folderID) + fileList, err := service.Files.List().Q(query).PageSize(10).Fields("nextPageToken, files(id, name, mimeType)").Do() + if err != nil { + return nil, err + } + return fileList.Files, nil +} + +func uploadFileToDrive(srv *drive.Service, folderID string, file io.Reader, fileName string) (*drive.File, error) { + fileMetadata := &drive.File{ + Name: fileName, + Parents: []string{folderID}, + } + + // Create the file in Google Drive + res, err := srv.Files.Create(fileMetadata).Media(file).Do() + if err != nil { + return nil, fmt.Errorf("cannot create file: %v", err) + } + + return res, nil +} diff --git a/firebase.go b/firebase.go index 5cdbe7d..409c9f1 100644 --- a/firebase.go +++ b/firebase.go @@ -6,7 +6,7 @@ import ( "log" "os" - "github.com/joho/godotenv" + //"github.com/joho/godotenv" firebase "firebase.google.com/go" "google.golang.org/api/option" @@ -14,17 +14,17 @@ import ( // Use godot package to load/read the .env file and // return the value of the key (for local env) -func goDotEnvVariable(key string) string { +// func goDotEnvVariable(key string) string { - // load .env file - err := godotenv.Load(".env") +// // load .env file +// err := godotenv.Load(".env") - if err != nil { - log.Fatalf("Error loading .env file") - } +// if err != nil { +// log.Fatalf("Error loading .env file") +// } - return os.Getenv(key) -} +// return os.Getenv(key) +// } // InitializeFirebase initializes the Firebase app and sets the global firebaseClient variable func initializeFirebase() error { diff --git a/go.mod b/go.mod index 949ebac..20ad418 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module EduSync +module edusync go 1.22.3 @@ -7,28 +7,28 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/sessions v1.3.0 - github.com/joho/godotenv v1.5.1 github.com/markbates/goth v1.80.0 - google.golang.org/api v0.188.0 + golang.org/x/oauth2 v0.21.0 + google.golang.org/api v0.189.0 ) require ( cloud.google.com/go v0.115.0 // indirect - cloud.google.com/go/auth v0.7.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect - cloud.google.com/go/compute/metadata v0.4.0 // indirect - cloud.google.com/go/firestore v1.15.0 // indirect + cloud.google.com/go/auth v0.7.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect + cloud.google.com/go/firestore v1.16.0 // indirect cloud.google.com/go/iam v1.1.10 // indirect cloud.google.com/go/longrunning v0.5.9 // indirect - cloud.google.com/go/storage v1.41.0 // indirect + cloud.google.com/go/storage v1.43.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.5 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect @@ -38,15 +38,14 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect + google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect google.golang.org/grpc v1.64.1 // indirect google.golang.org/protobuf v1.34.2 // indirect ) diff --git a/go.sum b/go.sum index b6c6a64..2eab768 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,20 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= -cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts= -cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw= -cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= -cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= -cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c= -cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M= -cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8= -cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= +cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE= +cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= +cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= +cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc= +cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg= cloud.google.com/go/iam v1.1.10 h1:ZSAr64oEhQSClwBL670MsJAW5/RLiC6kfw3Bqmd5ZDI= cloud.google.com/go/iam v1.1.10/go.mod h1:iEgMq62sg8zx446GCaijmA2Miwg5o3UbO+nI47WHJps= cloud.google.com/go/longrunning v0.5.9 h1:haH9pAuXdPAMqHvzX0zlWQigXT7B0+CL4/2nXXdBo5k= cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c= -cloud.google.com/go/storage v1.41.0 h1:RusiwatSu6lHeEXe3kglxakAmAbfV+rhtPqA6i8RBx0= -cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= +cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= +cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -31,8 +31,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -73,16 +73,14 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA= -github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8= github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -170,10 +168,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw= -google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag= +google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI= +google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -181,12 +177,12 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b h1:dSTjko30weBaMj3eERKc0ZVXW4GudCswM3m+P++ukU0= -google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b h1:04+jVzTs2XBnOZcPsLnmrTGqltqJbZQ1Ey26hjYdQQ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg= +google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= +google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade h1:WxZOF2yayUHpHSbUE6NMzumUzBxYc3YGwo0YHnbzsJY= +google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= diff --git a/instructorHandler.go b/instructorHandler.go index 8c8a89a..729b256 100644 --- a/instructorHandler.go +++ b/instructorHandler.go @@ -16,4 +16,69 @@ func InstructorHandler(router *mux.Router) { } t.Execute(res, nil) }).Methods("GET") + + router.HandleFunc("/instructor/learning-materials/classes", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/instructor/lm-classes.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, nil) + }).Methods("GET") + + router.HandleFunc("/instructor/classes/get-classes", func(res http.ResponseWriter, req *http.Request) { + GetInstructorClasses(res, req) + }).Methods("GET") + + router.HandleFunc("/instructor/learning-materials/upload", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/instructor/lm-upload.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, nil) + }).Methods("GET") + + router.HandleFunc("/instructor/media/classes", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/instructor/media-classes.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, nil) + }).Methods("GET") + + router.HandleFunc("/instructor/media/get-classes", func(res http.ResponseWriter, req *http.Request) { + GetInstructorClassIds(res, req) + }).Methods("GET") + + router.HandleFunc("/instructor/api/media", func(res http.ResponseWriter, req *http.Request) { + classId := req.URL.Query().Get("class_id") + GetStudentsAndFoldersByClassID(classId, res, req) + }).Methods("GET") + + router.HandleFunc("/instructor/media/upload", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/instructor/media-upload.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, nil) + }).Methods("GET") + + router.HandleFunc("/instructor/profile", func(res http.ResponseWriter, req *http.Request) { + instructor, err := GetCurrentInstructor(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + // Render the profile page + t, err := template.ParseFiles("templates/instructor/profile.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, instructor) + }).Methods("GET") } diff --git a/main.go b/main.go index 3de124c..7f234b8 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ func main() { // Set up authentication routes AuthHandler(router, config) MainHandler(router) + DriveHandler(router) log.Println("listening on localhost:8080") err = http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", router) diff --git a/mainHandler.go b/mainHandler.go index da44a74..78ed36e 100644 --- a/mainHandler.go +++ b/mainHandler.go @@ -1,5 +1,7 @@ +// This file contains the main handler for the web application. package main +//The import section declares packages that this source file depends on. import ( "html/template" "net/http" @@ -7,6 +9,7 @@ import ( "github.com/gorilla/mux" ) +// MainHandler handles the main page of the web application. func MainHandler(router *mux.Router) { router.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { t, err := template.ParseFiles("templates/index.html") @@ -25,4 +28,40 @@ func MainHandler(router *mux.Router) { } t.Execute(res, false) }).Methods("GET") + + router.HandleFunc("/student-role", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/student.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, false) + }).Methods("GET") + + router.HandleFunc("/admin-role", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/admin.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, false) + }).Methods("GET") + + router.HandleFunc("/parent-role", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/parent.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, false) + }).Methods("GET") + + router.HandleFunc("/instructor-role", func(res http.ResponseWriter, req *http.Request) { + t, err := template.ParseFiles("templates/instructor.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, false) + }).Methods("GET") } diff --git a/parentHandler.go b/parentHandler.go index 8c97886..3f2ce0f 100644 --- a/parentHandler.go +++ b/parentHandler.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "html/template" "net/http" @@ -16,4 +17,34 @@ func ParentHandler(router *mux.Router) { } t.Execute(res, nil) }).Methods("GET") + + router.HandleFunc("/parent/profile", func(res http.ResponseWriter, req *http.Request) { + parent, err := GetCurrentParent(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + // Render the profile page + t, err := template.ParseFiles("templates/parent/profile.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, parent) + }).Methods("GET") + + router.HandleFunc("/parent/get-folder-id", func(res http.ResponseWriter, req *http.Request) { + parent, err := GetCurrentParent(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + response := map[string]string{"folder_id": parent.FolderID} + res.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(res).Encode(response); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + } + }).Methods("GET") } diff --git a/studentHandler.go b/studentHandler.go index 1276a1c..f859343 100644 --- a/studentHandler.go +++ b/studentHandler.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "html/template" "net/http" @@ -16,4 +17,33 @@ func StudentHandler(router *mux.Router) { } t.Execute(res, nil) }).Methods("GET") + + router.HandleFunc("/student/get-folder-id", func(res http.ResponseWriter, req *http.Request) { + folderID, err := GetStudentFolder(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + response := map[string]string{"folder_id": folderID} + res.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(res).Encode(response); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + } + }).Methods("GET") + + router.HandleFunc("/student/profile", func(res http.ResponseWriter, req *http.Request) { + student, err := GetCurrentStudent(req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + t, err := template.ParseFiles("templates/student/profile.html") + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + t.Execute(res, student) + }).Methods("GET") } diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..c1ec815 --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html> +<head> + <title>EduSync - Admins</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + h2 { + font-size: 2em; /* Adjust size as needed */ + font-weight: bold; + margin-top: 20px; + margin-bottom: 20px; + } + + p { + font-size: 1em; /* Adjust size as needed */ + line-height: 1.6; + margin-bottom: 20px; + } + + /* Content area that takes up remaining space */ + .content { + flex: 1; + padding: 20px; /* Add padding if needed */ + max-width: 1200px; /* Limit the width of content */ + margin: 0 auto; /* Center the content */ + } + + .queries { + margin-top: 40px; + padding: 20px; + background-color: #FFBE0B; + color: #000; + border-radius: 8px; + text-align: center; + } + + .queries a { + color: #246EB9; /* Example color for links */ + text-decoration: none; + font-weight: bold; + } + + a:hover { + color: #4A90E2; /* Lighter shade of blue */ + text-decoration: underline; /* Add underline on hover (optional) */ + } + + /* Slogan styling */ + .slogan { + font-size: 2em; + font-weight: bold; + color: #FFBE0B; /* Example color for the slogan */ + text-align: center; + margin-top: 30px; + margin-bottom: 30px; + font-style: italic; + } + + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <!-- Header --> + <header class="site-header"> + <div class="top-header"> + <div class="container"> + <div class="top-header-left"> + <div class="top-header-block"> + <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> + </div> + <div class="top-header-block"> + <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> + </div> + </div> + <div class="top-header-right"> + <div class="social-block"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + </ul> + </div> + </div> + </div> + </div> + <!-- Top header Close --> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/">Home</a></li> + <li><a href="/admin-role">Admin</a></li> + <li><a href="/parent-role">Parent</a></li> + <li><a href="/student-role">Student</a></li> + <li><a href="/instructor-role">Instructor</a></li> + <li><a href="/login">Login</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <!-- Content --> + <div class="content"> + <!-- Main content --> + <h2>Admin Role</h2> + <p>Admins can use EduSync to edit details for students, parents, and instructors, and create announcements for parents. This platform enables admins to manage all the data and communication in an organized manner.</p> + + <!-- Slogan --> + <div class="slogan"> + EduSync: Connect, Learn, Succeed + </div> + + <!-- Queries section --> + <div class="queries"> + <p><strong>If you have any queries, please email us at <a target="_blank" href="mailto:nkedusync@gmail.com">nkedusync@gmail.com</a>. + <br> + <br> + To sign up, please find more information at the <a target="_blank" href="https://nkr.sg/">NK Robotics Website</a>.</strong></p> + </div> + </div> + <br> + + <!-- Footer --> + <footer class="page-footer" itemprop="footer" itemscope itemtype="http://schema.org/WPFooter"> + <div class="footer-first-section"> + <div class="container"> + <div class="box-wrap" itemprop="about"> + <header> + <h1>about</h1> + </header> + <p>EduSync is a great start for remote learning. Created for students, teachers, and parents.</p> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + </div> + + <div class="box-wrap"> + <header> + <h1>User Guides</h1> + </header> + <ul> + <li><a href="#">Students</a></li> + <li><a href="#">Parents</a></li> + <li><a href="#">Teachers</a></li> + </ul> + </div> + + <div class="box-wrap"> + <header> + <h1>quick contact</h1> + </header> + <section class="quick-contact"> + <input type="email" name="email" placeholder="Your Email*"> + <textarea placeholder="Type your message*"></textarea> + <button>send message</button> + </section> + </div> + </div> + </div> + <div class="footer-second-section"> + <div class="container"> + <hr class="footer-line"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> + </ul> + <hr class="footer-line"> + </div> + </div> + <div class="footer-last-section"> + <div class="container"> + <p>© 2024 EduSync. All rights reserved.</p> + </div> + </div> + </footer> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/admin/create_announcement.html b/templates/admin/create_announcement.html index 4fbe75c..986949b 100644 --- a/templates/admin/create_announcement.html +++ b/templates/admin/create_announcement.html @@ -61,6 +61,23 @@ } }; </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body onload="loadAnnouncement()"> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -78,6 +95,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -90,10 +109,11 @@ </div> </div> </header> + <hr><br><br> <!-- Header Close --> <div class="container content"> - <h1>Send Announcement</h1> + <h1>Send Announcement</h1><br> <form onsubmit="sendAnnouncement(event)"> <div><label>Subject: </label><input type="text" id="subject" required></div> <br> diff --git a/templates/admin/create_instructor.html b/templates/admin/create_instructor.html index 7f3c997..a0d03b6 100644 --- a/templates/admin/create_instructor.html +++ b/templates/admin/create_instructor.html @@ -44,6 +44,23 @@ } } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body> <div id="page" class="site"> @@ -61,15 +78,18 @@ <li><a href="/admin/search_student">Students</a></li> <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> </div> </div> </div> </header> + <hr><br><br> <div class="container content"> - <h1>Create New Instructor</h1> + <h1>Create New Instructor</h1><br> <form onsubmit="event.preventDefault(); createInstructor();"> <div><label>Name: </label><input type="text" id="name" required></div> <br> diff --git a/templates/admin/create_parent.html b/templates/admin/create_parent.html index 59135a9..98c1781 100644 --- a/templates/admin/create_parent.html +++ b/templates/admin/create_parent.html @@ -35,6 +35,23 @@ } } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body> <div id="page" class="site"> @@ -52,15 +69,20 @@ <li><a href="/admin/search_student">Students</a></li> <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> </div> </div> </div> </header> + <hr> + <br> + <br> <div class="container content"> - <h1>Create New Parent</h1> + <h1>Create New Parent</h1><br> <form onsubmit="event.preventDefault(); createParent();"> <div><label>Name: </label><input type="text" id="name" required></div> <br> diff --git a/templates/admin/create_student.html b/templates/admin/create_student.html index 20a01d2..337e099 100644 --- a/templates/admin/create_student.html +++ b/templates/admin/create_student.html @@ -46,6 +46,23 @@ } } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -63,6 +80,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -75,10 +94,14 @@ </div> </div> </header> + <hr> <!-- Header Close --> + <br> + <br> <div class="container content"> <h1>Create New Student</h1> + <br> <form onsubmit="event.preventDefault(); createStudent();"> <div><label>Name: </label><input type="text" id="name" required></div> <br> diff --git a/templates/admin/edit_announcement.html b/templates/admin/edit_announcement.html index 4b543fa..bd1e73d 100644 --- a/templates/admin/edit_announcement.html +++ b/templates/admin/edit_announcement.html @@ -60,6 +60,23 @@ window.history.back(); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body onload="loadAnnouncement()"> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -77,6 +94,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -89,10 +108,13 @@ </div> </div> </header> + <hr> <!-- Header Close --> - + <br> + <br> <div class="container content"> <h1>Edit Announcement Details</h1> + <br> <input type="hidden" id="announcementID" value="{{.AnnouncementID}}"> <form onsubmit="event.preventDefault(); updateAnnouncement();"> <div><label>Subject: </label><input type="text" id="subject" disabled></div> diff --git a/templates/admin/edit_instructor.html b/templates/admin/edit_instructor.html index 46a16f1..666867e 100644 --- a/templates/admin/edit_instructor.html +++ b/templates/admin/edit_instructor.html @@ -1,12 +1,12 @@ <!DOCTYPE html> <html> <head> - <title>Edit Instructor Details</title> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> - <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <title>Edit Instructor Details</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> <script> - let initialStudent = {}; + let initialInstructor = {}; async function loadInstructor() { const googleID = document.getElementById('googleID').value; @@ -16,25 +16,22 @@ document.getElementById('contactNumber').value = instructor.contact_number; document.getElementById('email').value = instructor.email; document.getElementById('basePay').value = instructor.base_pay; - document.getElementById('role').value = instructor.role; - initialStudent = {...student} + initialInstructor = {...instructor}; } function hasChanges() { return ( - document.getElementById('name').value !== initialStudent.name || - document.getElementById('email').value !== initialStudent.email || - parseInt(document.getElementById('age').value, 10) !== initialStudent.age || - document.getElementById('class').value !== initialStudent.class || - document.getElementById('instructor').value !== initialStudent.instructor || - document.getElementById('parentName').value !== initialStudent.parent_name || - document.getElementById('role').value !== initialStudent.role + document.getElementById('name').value !== initialInstructor.name || + document.getElementById('contactNumber').value !== initialInstructor.contact_number || + document.getElementById('email').value !== initialInstructor.email || + parseFloat(document.getElementById('basePay').value) !== initialInstructor.base_pay ); } async function updateInstructor() { const googleID = document.getElementById('googleID').value; + if (!hasChanges()) { return; // No changes, so do not proceed with update } @@ -42,8 +39,7 @@ name: document.getElementById('name').value, contact_number: document.getElementById('contactNumber').value, email: document.getElementById('email').value, - base_pay: parseFloat(document.getElementById('basePay').value), - role: document.getElementById('role').value + base_pay: parseFloat(document.getElementById('basePay').value) }; await fetch(`/admin/instructor/${googleID}`, { method: 'PUT', @@ -79,27 +75,62 @@ } } + function validateEmail() { + const email = document.getElementById('email').value; + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const errorElement = document.getElementById('emailError'); + + if (emailPattern.test(email)) { + errorElement.textContent = ''; // Clear error message + document.getElementById('saveButton').disabled = false; // Enable save button + } else { + errorElement.textContent = 'Please enter a valid email address.'; + document.getElementById('saveButton').disabled = true; // Disable save button + } + } + function goBack() { window.history.back(); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body onload="loadInstructor()"> - <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> - <header class="site-header"> - <div class="main-header"> - <div class="container"> - <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> - </div> - <div class="nav-wrap"> - <nav class="nav-desktop"> - <ul class="menu-list"> - <li><a href="/admin">Home</a></li> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/admin">Home</a></li> <li><a href="/admin/search_student">Students</a></li> <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -112,25 +143,30 @@ </div> </div> </header> + <hr> + <br> + <br> <!-- Header Close --> <div class="container content"> <h1>Edit Instructor Details</h1> + <br> <input type="hidden" id="googleID" value="{{.GoogleID}}"> <form onsubmit="event.preventDefault(); updateInstructor();"> - <div><label>Name: </label><input type="text" id="name" disabled></div> + <div><label><strong>Name: </strong></label> <input type="text" id="name" disabled></div> <br> <div> - <label>Contact Number: </label> - <input type="text" id="contactNumber" disabled oninput="validatePhoneNumber()"> + <label><strong>Contact Number: </strong></label> + <input type="text" id="contactNumber" disabled oninput="validatePhoneNumber()"> <span id="phoneNumberError" class="error-message"></span> </div> <br> - <div><label>Email: </label><input type="text" id="email" disabled></div> - <br> - <div><label>Base Pay: </label><input type="number" step="0.01" id="basePay" disabled></div> - <br> - <div><label>Role: </label><input type="text" id="role" disabled></div> + <div> + <label><strong>Email: </strong></label> + <input type="text" id="email" disabled oninput="validateEmail()"> + <span id="emailError" class="error-message"></span> + </div> <br> + <div><label><strong>Base Pay: </strong></label> <input type="number" step="0.01" id="basePay" disabled></div> <br> <div class="button-container"> <button type="button" id="editButton" onclick="toggleEdit()">Edit</button> @@ -139,8 +175,8 @@ <h1>Edit Instructor Details</h1> </div> </form> </div> - </div> - <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> - <script type="text/javascript" src="/assets/js/custom.js"></script> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> </body> -</html> \ No newline at end of file +</html> diff --git a/templates/admin/edit_parent.html b/templates/admin/edit_parent.html index 98d0f40..a8fdda1 100644 --- a/templates/admin/edit_parent.html +++ b/templates/admin/edit_parent.html @@ -1,12 +1,15 @@ <!DOCTYPE html> <html> <head> - <title>Edit Parent Details</title> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> - <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <title>EduSync</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> <script> - let initialStudent = {}; + let initialParent = {}; async function loadParent() { const googleID = document.getElementById('googleID').value; @@ -14,35 +17,36 @@ const parent = await response.json(); document.getElementById('name').value = parent.name; document.getElementById('email').value = parent.email; - document.getElementById('contactNumber').value = parent.contact_no; - document.getElementById('role').value = parent.role; - - initialStudent = {...student} + document.getElementById('contactNumber').value = parent.contact_number; + document.getElementById('folderID').value = parent.folder_id; + + initialParent = { ...parent }; } function hasChanges() { return ( - document.getElementById('name').value !== initialStudent.name || - document.getElementById('email').value !== initialStudent.email || - parseInt(document.getElementById('age').value, 10) !== initialStudent.age || - document.getElementById('class').value !== initialStudent.class || - document.getElementById('instructor').value !== initialStudent.instructor || - document.getElementById('parentName').value !== initialStudent.parent_name || - document.getElementById('role').value !== initialStudent.role + document.getElementById('name').value !== initialParent.name || + document.getElementById('email').value !== initialParent.email || + document.getElementById('contactNumber').value !== initialParent.contact_number || + document.getElementById('folderID').value !== initialParent.folder_id ); } async function updateParent() { const googleID = document.getElementById('googleID').value; + const contactNumber = document.getElementById('contactNumber').value; + if (!hasChanges()) { return; // No changes, so do not proceed with update } + const updates = { name: document.getElementById('name').value, email: document.getElementById('email').value, - contact_no: document.getElementById('contactNumber').value, - role: document.getElementById('role').value + contact_number: contactNumber, + folder_id: document.getElementById('folderID').value }; + await fetch(`/admin/parent/${googleID}`, { method: 'PUT', headers: { @@ -50,6 +54,7 @@ }, body: JSON.stringify(updates) }); + alert('Parent updated successfully'); window.location.href = '/admin/search_parent'; } @@ -76,28 +81,63 @@ document.getElementById('saveButton').disabled = true; // Disable save button } } + + function validateEmail() { + const email = document.getElementById('email').value; + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const errorElement = document.getElementById('emailError'); + + if (emailPattern.test(email)) { + errorElement.textContent = ''; // Clear error message + document.getElementById('saveButton').disabled = false; // Enable save button + } else { + errorElement.textContent = 'Please enter a valid email address.'; + document.getElementById('saveButton').disabled = true; // Disable save button + } + } function goBack() { window.history.back(); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body onload="loadParent()"> - <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> - <header class="site-header"> - <div class="main-header"> - <div class="container"> - <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> - </div> - <div class="nav-wrap"> - <nav class="nav-desktop"> - <ul class="menu-list"> - <li><a href="/admin">Home</a></li> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/admin">Home</a></li> <li><a href="/admin/search_student">Students</a></li> <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -110,33 +150,46 @@ </div> </div> </header> + <hr> <!-- Header Close --> - + <br> + <br> <div class="container content"> <h1>Edit Parent Details</h1> + <br> <input type="hidden" id="googleID" value="{{.GoogleID}}"> <form onsubmit="event.preventDefault(); updateParent();"> - <div><label>Name: </label><input type="text" id="name" disabled></div> + <div><label><strong>Name: </strong></label> <input type="text" id="name" disabled></div> <br> - <div><label>Email: </label><input type="text" id="email" disabled></div> + <div> + <label><strong>Email: </strong></label> + <input type="text" id="email" disabled oninput="validateEmail()"> + <span id="emailError" class="error-message"></span> + </div> <br> <div> - <label>Contact Number: </label> - <input type="text" id="contactNumber" disabled oninput="validatePhoneNumber()"> + <label><strong>Contact Number: </strong></label> + <input type="text" id="contactNumber" disabled oninput="validatePhoneNumber()"> <span id="phoneNumberError" class="error-message"></span> </div> <br> - <div><label>Role: </label><input type="text" id="role" disabled></div> + <div><label><strong>Folder ID: </strong></label> <input type="text" id="folderID" disabled></div> + <br> <br> <div class="button-container"> <button type="button" id="editButton" onclick="toggleEdit()">Edit</button> <button type="submit" id="saveButton" style="display:none;">Save</button> <button type="button" onclick="goBack()">Back</button> - </div> + </div> </form> </div> - </div> - <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> - <script type="text/javascript" src="/assets/js/custom.js"></script> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> </body> </html> \ No newline at end of file diff --git a/templates/admin/edit_student.html b/templates/admin/edit_student.html index ebe1719..b92d861 100644 --- a/templates/admin/edit_student.html +++ b/templates/admin/edit_student.html @@ -22,7 +22,6 @@ document.getElementById('lessonCredits').value = student.lesson_credits; document.getElementById('classID').value = student.class_id; document.getElementById('parentID').value = student.parent_id; - document.getElementById('role').value = student.role; initialStudent = { ...student }; } @@ -35,31 +34,35 @@ parseInt(document.getElementById('age').value, 10) !== initialStudent.age || parseFloat(document.getElementById('lessonCredits').value) !== initialStudent.lesson_credits || document.getElementById('classID').value !== initialStudent.class_id || - document.getElementById('parentID').value !== initialStudent.parent_id || - document.getElementById('role').value !== initialStudent.role + document.getElementById('parentID').value !== initialStudent.parent_id ); } async function updateStudent() { const googleID = document.getElementById('googleID').value; const age = document.getElementById('age').value; + const contactNumber = document.getElementById('contactNumber').value; + + // Validate age if (age < 4 || age > 100) { alert('Age must be between 4 and 100.'); return; } + if (!hasChanges()) { return; // No changes, so do not proceed with update } + const updates = { name: document.getElementById('name').value, email: document.getElementById('email').value, - contact_number: document.getElementById('contactNumber').value, + contact_number: contactNumber, age: parseInt(age, 10), lesson_credits: parseFloat(document.getElementById('lessonCredits').value), class_id: document.getElementById('classID').value, - parent_id: document.getElementById('parentID').value, - role: document.getElementById('role').value + parent_id: document.getElementById('parentID').value }; + await fetch(`/admin/student/${googleID}`, { method: 'PUT', headers: { @@ -67,6 +70,7 @@ }, body: JSON.stringify(updates) }); + alert('Student updated successfully'); window.location.href = '/admin/search_student'; } @@ -80,10 +84,59 @@ document.getElementById('saveButton').style.display = 'block'; } + + function validatePhoneNumber() { + const phoneNumber = document.getElementById('contactNumber').value; + const phoneNumberPattern = /^[689]\d{7}$/; + const errorElement = document.getElementById('phoneNumberError'); + + if (phoneNumberPattern.test(phoneNumber)) { + errorElement.textContent = ''; // Clear error message + document.getElementById('saveButton').disabled = false; // Enable save button + } else { + errorElement.textContent = 'Phone number must start with 6, 8, or 9 and have 8 digits.'; + document.getElementById('saveButton').disabled = true; // Disable save button + } + } + + + function validateEmail() { + const email = document.getElementById('email').value; + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const errorElement = document.getElementById('emailError'); + + if (emailPattern.test(email)) { + errorElement.textContent = ''; // Clear error message + document.getElementById('saveButton').disabled = false; // Enable save button + } else { + errorElement.textContent = 'Please enter a valid email address.'; + document.getElementById('saveButton').disabled = true; // Disable save button + } + } + + function goBack() { window.history.back(); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + + .error-message { + color: red; + } + </style> </head> <body onload="loadStudent()"> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -91,7 +144,9 @@ <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> </div> <div class="nav-wrap"> <nav class="nav-desktop"> @@ -101,6 +156,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -113,27 +170,38 @@ </div> </div> </header> + <hr> <!-- Header Close --> + <br> + <br> <div class="container content"> <h1>Edit Student Details</h1> + <br> <input type="hidden" id="googleID" value="{{.GoogleID}}"> <form onsubmit="event.preventDefault(); updateStudent();"> - <div><label>Name: </label><input type="text" id="name" disabled></div> + <div><label><strong>Name: </strong></label> <input type="text" id="name" disabled></div> <br> - <div><label>Email: </label><input type="text" id="email" disabled></div> + <div> + <label><strong>Email: </strong></label> + <input type="text" id="email" disabled oninput="validateEmail()"> + <span id="emailError" class="error-message"></span> + </div> <br> - <div><label>Contact Number: </label><input type="text" id="contactNumber" disabled></div> + <div> + <label><strong>Contact Number: </strong></label> + <input type="text" id="contactNumber" disabled oninput="validatePhoneNumber()"> + <span id="phoneNumberError" class="error-message"></span> + </div> <br> - <div><label>Age: </label><input type="number" id="age" min="4" max="100" disabled></div> + <div><label><strong>Age: </strong></label> <input type="number" id="age" min="4" max="100" disabled></div> <br> - <div><label>Lesson Credits: </label><input type="number" id="lessonCredits" step="0.1" disabled></div> + <div><label><strong>Lesson Credits: </strong></label> <input type="number" id="lessonCredits" step="0.1" disabled></div> <br> - <div><label>Class ID: </label><input type="text" id="classID" disabled></div> + <div><label><strong>Class ID: </strong></label> <input type="text" id="classID" disabled></div> <br> - <div><label>Parent ID: </label><input type="text" id="parentID" disabled></div> + <div><label><strong>Parent ID: </strong></label> <input type="text" id="parentID" disabled></div> <br> - <div><label>Role: </label><input type="text" id="role" disabled></div> <br> <div class="button-container"> <button type="button" id="editButton" onclick="toggleEdit()">Edit</button> @@ -151,4 +219,4 @@ <h1>Edit Student Details</h1> <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> <script type="text/javascript" src="/assets/js/custom.js"></script> </body> -</html> \ No newline at end of file +</html> diff --git a/templates/admin/index.html b/templates/admin/index.html index 8daa503..b5129be 100644 --- a/templates/admin/index.html +++ b/templates/admin/index.html @@ -8,6 +8,11 @@ <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -15,8 +20,9 @@ <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> - <!-- <h1>Education</h1> --> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> </div> <div class="nav-wrap"> <nav class="nav-desktop"> @@ -26,6 +32,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -69,9 +77,9 @@ <h1>About EduSync for Admins</h1> </header> <p>EduSync provides administrative tools and functionalities to streamline educational management tasks.</p> - <h4><a href="https://nkr.sg/"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> - <h4><a href="mailto:nkedusync@gmail.com"><i class="fas fa-envelope"></i> Contact Us</a></h4> - <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> Contact Us</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> </div> <div class="box-wrap"> @@ -103,11 +111,11 @@ <h1>Quick Contact</h1> <div class="container"> <hr class="footer-line"> <ul class="social-list"> - <li><a href=""><i class="fab fa-facebook-square"></i></a></li> - <li><a href=""><i class="fab fa-twitter"></i></a></li> - <li><a href=""><i class="fab fa-skype"></i></a></li> - <li><a href=""><i class="fab fa-youtube"></i></a></li> - <li><a href=""><i class="fab fa-instagram"></i></a></li> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> </ul> <hr class="footer-line"> </div> diff --git a/templates/admin/profile.html b/templates/admin/profile.html new file mode 100644 index 0000000..6141549 --- /dev/null +++ b/templates/admin/profile.html @@ -0,0 +1,191 @@ +<!DOCTYPE html> +<html> +<head> + <title>Edit Admin Profile</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <script> + let initialAdmin = {}; + + async function loadAdmin() { + const response = await fetch(`/admin/api/profile`); + const admin = await response.json(); + document.getElementById('name').value = admin.name; + document.getElementById('contactNumber').value = admin.contact_number; + document.getElementById('email').value = admin.email; + document.getElementById('basePay').value = admin.base_pay; + document.getElementById('incentive').value = admin.incentive; + + initialAdmin = { ...admin }; + } + + function hasChanges() { + return ( + document.getElementById('name').value !== initialAdmin.name || + document.getElementById('contactNumber').value !== initialAdmin.contact_number || + document.getElementById('email').value !== initialAdmin.email || + parseFloat(document.getElementById('basePay').value) !== initialAdmin.base_pay || + parseFloat(document.getElementById('incentive').value) !== initialAdmin.incentive + ); + } + + async function updateAdmin() { + if (!hasChanges()) { + return; // No changes, so do not proceed with update + } + const updates = { + name: document.getElementById('name').value, + contact_number: document.getElementById('contactNumber').value, + email: document.getElementById('email').value, + base_pay: parseFloat(document.getElementById('basePay').value), + incentive: parseFloat(document.getElementById('incentive').value) + }; + await fetch(`/admin/api/profile`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(updates) + }); + alert('Admin profile updated successfully'); + window.location.href = '/admin/profile'; + } + + function toggleEdit() { + const inputs = document.querySelectorAll('input'); + inputs.forEach(input => { + input.disabled = !input.disabled; + }); + document.getElementById('editButton').style.display = 'none'; + document.getElementById('saveButton').style.display = 'block'; + } + + function validatePhoneNumber() { + const phoneNumber = document.getElementById('contactNumber').value; + const phoneNumberPattern = /^[689]\d{7}$/; + const errorElement = document.getElementById('phoneNumberError'); + + if (phoneNumberPattern.test(phoneNumber)) { + errorElement.textContent = ''; // Clear error message + document.getElementById('saveButton').disabled = false; // Enable save button + } else { + errorElement.textContent = 'Phone number must start with 6, 8, or 9 and have 8 digits.'; + document.getElementById('saveButton').disabled = true; // Disable save button + } + } + + function validateEmail() { + const email = document.getElementById('email').value; + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const errorElement = document.getElementById('emailError'); + + if (emailPattern.test(email)) { + errorElement.textContent = ''; // Clear error message + document.getElementById('saveButton').disabled = false; // Enable save button + } else { + errorElement.textContent = 'Please enter a valid email address.'; + document.getElementById('saveButton').disabled = true; // Disable save button + } + } + + function goBack() { + window.history.back(); + } + </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> +</head> +<body onload="loadAdmin()"> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/admin">Home</a></li> + <li><a href="/admin/search_student">Students</a></li> + <li><a href="/admin/search_parent">Parents</a></li> + <li><a href="/admin/search_instructor">Instructors</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <br> + <br> + <div class="container content"> + <h1>Edit Admin Profile</h1> + <br> + <form onsubmit="event.preventDefault(); updateAdmin();"> + <div><label><strong>Name: </strong></label> <input type="text" id="name" name="name" disabled></div> + <br> + <div> + <label><strong>Email: </strong></label> + <input type="text" id="email" name="email" disabled oninput="validateEmail()"> + <span id="emailError" class="error-message"></span> + </div> + <br> + <div> + <label><strong>Contact Number: </strong></label> + <input type="text" id="contactNumber" name="contact_number" disabled oninput="validatePhoneNumber()"> + <span id="phoneNumberError" class="error-message"></span> + </div> + <br> + <div><label><strong>Base Pay: </strong></label> <input type="number" step="0.01" id="basePay" name="base_pay" disabled></div> + <br> + <div><label><strong>Incentive: </strong></label> <input type="number" step="0.01" id="incentive" name="incentive" disabled></div> + <br> + <div class="button-container"> + <button type="button" id="editButton" onclick="toggleEdit()">Edit</button> + <button type="submit" id="saveButton" style="display:none;">Save</button> + <button type="button" onclick="goBack()">Back</button> + </div> + </form> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> diff --git a/templates/admin/search_announcement.html b/templates/admin/search_announcement.html index 16faf70..6c9e1d9 100644 --- a/templates/admin/search_announcement.html +++ b/templates/admin/search_announcement.html @@ -74,6 +74,23 @@ await fetchAnnouncements(`/admin/api/search_announcement`); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + .error-message { + color: red; + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -106,8 +123,11 @@ </header> <hr> <!-- Header Close --> + <br> + <br> <div class="container content"> <h1>Search Announcements</h1> + <br> <div> <label for="subjectFilter">subject:</label> <input type="text" id="subjectFilter"> diff --git a/templates/admin/search_instructor.html b/templates/admin/search_instructor.html index e6cd837..ea5f645 100644 --- a/templates/admin/search_instructor.html +++ b/templates/admin/search_instructor.html @@ -54,14 +54,32 @@ resultsDiv.appendChild(instructorDiv); }); } else { - resultsDiv.innerHTML = 'No instructors found.'; + resultsDiv.innerHTML = '<strong>No instructors found.</strong>'; } } + + async function resetFilters() { + document.getElementById('nameFilter').value = ''; + await fetchInstructors(`/admin/api/search_instructor`); + } window.onload = async function() { await fetchInstructors(`/admin/api/search_instructor`); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -69,7 +87,9 @@ <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> <!-- <h1>Education</h1> --> </div> <div class="nav-wrap"> @@ -80,6 +100,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -94,12 +116,19 @@ </header> <hr> <!-- Header Close --> + <br> + <br> <div class="container content"> <h1>Search Instructors</h1> + <br> <div> <label for="nameFilter">Name:</label> <input type="text" id="nameFilter"> + <button onclick="searchInstructors()">Search</button> + + <button onclick="resetFilters()">Reset</button> + <button onclick="window.location.href='/admin/create_instructor'">Create</button> </div> <br> diff --git a/templates/admin/search_parent.html b/templates/admin/search_parent.html index 73e5adf..7854ec9 100644 --- a/templates/admin/search_parent.html +++ b/templates/admin/search_parent.html @@ -54,14 +54,32 @@ resultsDiv.appendChild(parentDiv); }); } else { - resultsDiv.innerHTML = 'No parents found.'; + resultsDiv.innerHTML = '<strong>No parents found.</strong>'; } } + async function resetFilters() { + document.getElementById('nameFilter').value = ''; + await fetchParents(`/admin/api/search_parent`); + } + window.onload = async function() { await fetchParents(`/admin/api/search_parent`); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -69,7 +87,9 @@ <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> <!-- <h1>Education</h1> --> </div> <div class="nav-wrap"> @@ -80,6 +100,8 @@ <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -94,12 +116,19 @@ </header> <hr> <!-- Header Close --> + <br> + <br> <div class="container content"> <h1>Search Parents</h1> + <br> <div> <label for="nameFilter">Name:</label> <input type="text" id="nameFilter"> + <button onclick="searchParents()">Search</button> + + <button onclick="resetFilters()">Reset</button> + <button onclick="window.location.href='/admin/create_parent'">Create</button> </div> <br> diff --git a/templates/admin/search_student.html b/templates/admin/search_student.html index e0d4c06..01f8063 100644 --- a/templates/admin/search_student.html +++ b/templates/admin/search_student.html @@ -1,15 +1,15 @@ <!DOCTYPE html> <html> <head> - <title>EduSync</title> - <meta name="viewport" content="width=device-width"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> - <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> - <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> - <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <title>EduSync</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> <script> - const daysOfWeek = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; + const daysOfWeek = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; function formatClassID(classID) { const parts = classID.split('-'); @@ -17,47 +17,21 @@ return classID; // Return as is if format is unexpected } const prefix = parts[0].toUpperCase(); - const day = daysOfWeek[parseInt(parts[1], 10)-1]; + const day = daysOfWeek[parseInt(parts[1], 10) - 1]; const hour = parseInt(parts[2], 10); const period = hour >= 12 ? 'pm' : 'am'; const formattedHour = hour > 12 ? hour - 12 : hour; return `${prefix}-${day}-${formattedHour}${period}`; } - async function fetchStudents(url) { - const response = await fetch(url); + async function fetchStudents(url) { + const response = await fetch(url); - if (!response.ok) { - const errorMessage = `Error: ${response.status} - ${response.statusText}`; - throw new Error(errorMessage); - } - - const students = await response.json(); - const resultsDiv = document.getElementById('results'); - resultsDiv.innerHTML = ''; - - if (students && students.length > 0) { - students.forEach(student => { - const studentDiv = document.createElement('div'); - studentDiv.className = 'student'; - studentDiv.innerHTML = `<a href="/admin/student/${student.google_id}/edit"><strong>${student.name}</strong></a> - <strong>${formatClassID(student.class_id)}</strong>`; - resultsDiv.appendChild(studentDiv); - }); - } else { - resultsDiv.innerHTML = 'No students found.'; - } - } - - async function searchStudents() { - const nameFilter = document.getElementById('nameFilter').value; - const classFilter = document.getElementById('classFilter').value; - const response = await fetch(`/admin/api/search_student?name=${nameFilter}&class=${classFilter}`); - if (!response.ok) { const errorMessage = `Error: ${response.status} - ${response.statusText}`; throw new Error(errorMessage); } - + const students = await response.json(); const resultsDiv = document.getElementById('results'); resultsDiv.innerHTML = ''; @@ -70,32 +44,60 @@ resultsDiv.appendChild(studentDiv); }); } else { - resultsDiv.innerHTML = 'No students found.'; + resultsDiv.innerHTML = '<strong>No students found.</strong>'; } } - window.onload = async function() { + async function searchStudents() { + const nameFilter = document.getElementById('nameFilter').value; + const classFilter = document.getElementById('classFilter').value; + await fetchStudents(`/admin/api/search_student?name=${nameFilter}&class=${classFilter}`); + } + + async function resetFilters() { + document.getElementById('nameFilter').value = ''; + document.getElementById('classFilter').value = ''; + await fetchStudents(`/admin/api/search_student`); + } + + window.onload = async function () { await fetchStudents(`/admin/api/search_student`); } </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> - <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> - <header class="site-header"> - <div class="main-header"> - <div class="container"> - <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> - <!-- <h1>Education</h1> --> - </div> - <div class="nav-wrap"> - <nav class="nav-desktop"> - <ul class="menu-list"> - <li><a href="/admin">Home</a></li> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/admin"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/admin">Home</a></li> <li><a href="/admin/search_student">Students</a></li> <li><a href="/admin/search_parent">Parents</a></li> <li><a href="/admin/search_instructor">Instructors</a></li> <li><a href="/admin/search_announcement">Announcements</a></li> + <li><a href="/admin/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -109,27 +111,35 @@ </div> </header> <hr> - <!-- Header Close --> - <div class="container content"> + <!-- Header Close --> + <br> + <br> + <div class="container content"> <h1>Update Student Details</h1> + <br> <div> <label for="nameFilter">Name:</label> <input type="text" id="nameFilter"> + <label for="classFilter">Class:</label> <input type="text" id="classFilter"> + <button onclick="searchStudents()">Search</button> + + <button onclick="resetFilters()">Reset</button> + <button onclick="window.location.href='/admin/create_student'">Create</button> </div> - <br> + <br> <div id="results"></div> </div> - </div> - <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> - <script type="text/javascript" src="/assets/js/all.js"></script> - <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> - <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> - <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> - <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> - <script type="text/javascript" src="/assets/js/custom.js"></script> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> </body> -</html> \ No newline at end of file +</html> diff --git a/templates/index.html b/templates/index.html index e766e9d..cd3f120 100644 --- a/templates/index.html +++ b/templates/index.html @@ -8,6 +8,12 @@ <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + a:hover { + color: #4A90E2; /* Lighter shade of blue */ + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -19,16 +25,16 @@ <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> </div> <div class="top-header-block"> - <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> + <a href="https://nkr.sg/" itemprop="url" target="_blank"><i class="fas fa-globe"></i> Visit Website</a> </div> </div> <div class="top-header-right"> <div class="social-block"> <ul class="social-list"> - <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes"><i class="fab fa-viber"></i></a></li> - <li><a href="https://www.facebook.com/NKRobotics/"><i class="fab fa-facebook-square"></i></a></li> - <li><a href="https://www.instagram.com/nk.robotics/"><i class="fab fa-instagram"></i></a></li> - <li><a href="https://sg.linkedin.com/company/nk-robotics"><i class="fab fa-linkedin"></i></a></li> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> </ul> </div> </div> @@ -37,28 +43,30 @@ <!-- Top header Close --> <div class="main-header"> <div class="container"> - <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> - <!-- <h1>Education</h1> --> - </div> - <div class="nav-wrap"> - <nav class="nav-desktop"> + <div class="logo-wrap" itemprop="logo"> + <a href="/"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> <ul class="menu-list"> - <li><a href="/admin">Admin</a></li> - <li><a href="/parent">Parent</a></li> - <li><a href="/student">Student</a></li> - <li><a href="/instructor">Instructor</a></li> - <li><a href="login">Login/Register</a></li> - </ul> - </nav> - <div id="bar"> - <i class="fas fa-bars"></i> - </div> - <div id="close"> - <i class="fas fa-times"></i> - </div> - </div> - </div> + <li><a href="/">Home</a></li> + <li><a href="/admin-role">Admin</a></li> + <li><a href="/parent-role">Parent</a></li> + <li><a href="/student-role">Student</a></li> + <li><a href="/instructor-role">Instructor</a></li> + <li><a href="/login">Login</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> </div> </header> <!-- Header Close --> @@ -90,9 +98,9 @@ <h1>about</h1> </header> <p>EduSync is a great start for remote learning. Created for students, teachers, and parents.</p> - <h4><a href="https://nkr.sg/"><i class="fas fa-globe"></i> Visit Website</a></h4> - <h4><a href="mailto:nkedusync@gmail.com"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a></h4> - <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> </div> <div class="box-wrap"> @@ -124,11 +132,11 @@ <h1>quick contact</h1> <div class="container"> <hr class="footer-line"> <ul class="social-list"> - <li><a href=""><i class="fab fa-facebook-square"></i></a></li> - <li><a href=""><i class="fab fa-twitter"></i></a></li> - <li><a href=""><i class="fab fa-skype"></i></a></li> - <li><a href=""><i class="fab fa-youtube"></i></a></li> - <li><a href=""><i class="fab fa-instagram"></i></a></li> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> </ul> <hr class="footer-line"> </div> diff --git a/templates/instructor.html b/templates/instructor.html new file mode 100644 index 0000000..b6d57ab --- /dev/null +++ b/templates/instructor.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html> +<head> + <title>EduSync - Instructors</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + h2 { + font-size: 2em; /* Adjust size as needed */ + font-weight: bold; + margin-top: 20px; + margin-bottom: 20px; + } + + p { + font-size: 1em; /* Adjust size as needed */ + line-height: 1.6; + margin-bottom: 20px; + } + + /* Content area that takes up remaining space */ + .content { + flex: 1; + padding: 20px; /* Add padding if needed */ + max-width: 1200px; /* Limit the width of content */ + margin: 0 auto; /* Center the content */ + } + + .queries { + margin-top: 40px; + padding: 20px; + background-color: #FFBE0B; + color: #000; + border-radius: 8px; + text-align: center; + } + + .queries a { + color: #246EB9; /* Example color for links */ + text-decoration: none; + font-weight: bold; + } + + a:hover { + color: #4A90E2; /* Lighter shade of blue */ + text-decoration: underline; /* Add underline on hover (optional) */ + } + + /* Slogan styling */ + .slogan { + font-size: 2em; + font-weight: bold; + color: #FFBE0B; /* Example color for the slogan */ + text-align: center; + margin-top: 30px; + margin-bottom: 30px; + font-style: italic; + } + + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <!-- Header --> + <header class="site-header"> + <div class="top-header"> + <div class="container"> + <div class="top-header-left"> + <div class="top-header-block"> + <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> + </div> + <div class="top-header-block"> + <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> + </div> + </div> + <div class="top-header-right"> + <div class="social-block"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + </ul> + </div> + </div> + </div> + </div> + <!-- Top header Close --> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/">Home</a></li> + <li><a href="/admin-role">Admin</a></li> + <li><a href="/parent-role">Parent</a></li> + <li><a href="/student-role">Student</a></li> + <li><a href="/instructor-role">Instructor</a></li> + <li><a href="/login">Login</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <!-- Content --> + <div class="content"> + <!-- Main content --> + <h2>Instructor Role</h2> + <p>Instructors can use EduSync to upload learning materials, ensuring that students have access to the latest content. This platform helps instructors manage and share their educational resources efficiently.</p> + + <!-- Slogan --> + <div class="slogan"> + EduSync: Connect, Learn, Succeed + </div> + + <!-- Queries section --> + <div class="queries"> + <p><strong>If you have any queries, please email us at <a target="_blank" href="mailto:nkedusync@gmail.com">nkedusync@gmail.com</a>. + <br> + <br> + To sign up, please find more information at the <a target="_blank" href="https://nkr.sg/">NK Robotics Website</a>.</strong></p> + </div> + </div> + <br> + + <!-- Footer --> + <footer class="page-footer" itemprop="footer" itemscope itemtype="http://schema.org/WPFooter"> + <div class="footer-first-section"> + <div class="container"> + <div class="box-wrap" itemprop="about"> + <header> + <h1>about</h1> + </header> + <p>EduSync is a great start for remote learning. Created for students, teachers, and parents.</p> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + </div> + + <div class="box-wrap"> + <header> + <h1>User Guides</h1> + </header> + <ul> + <li><a href="#">Students</a></li> + <li><a href="#">Parents</a></li> + <li><a href="#">Teachers</a></li> + </ul> + </div> + + <div class="box-wrap"> + <header> + <h1>quick contact</h1> + </header> + <section class="quick-contact"> + <input type="email" name="email" placeholder="Your Email*"> + <textarea placeholder="Type your message*"></textarea> + <button>send message</button> + </section> + </div> + </div> + </div> + <div class="footer-second-section"> + <div class="container"> + <hr class="footer-line"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> + </ul> + <hr class="footer-line"> + </div> + </div> + <div class="footer-last-section"> + <div class="container"> + <p>© 2024 EduSync. All rights reserved.</p> + </div> + </div> + </footer> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/instructor/index.html b/templates/instructor/index.html index eb4c0a9..1b76860 100644 --- a/templates/instructor/index.html +++ b/templates/instructor/index.html @@ -8,64 +8,30 @@ <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> <header class="site-header"> - <div class="top-header"> - <div class="container"> - <div class="top-header-left"> - <div class="top-header-block"> - <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> - </div> - <div class="top-header-block"> - <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> - </div> - </div> - <div class="top-header-right"> - <div class="social-block"> - <ul class="social-list"> - <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes"><i class="fab fa-viber"></i></a></li> - <li><a href="https://www.facebook.com/NKRobotics/"><i class="fab fa-facebook-square"></i></a></li> - <li><a href="https://www.instagram.com/nk.robotics/"><i class="fab fa-instagram"></i></a></li> - <li><a href="https://sg.linkedin.com/company/nk-robotics"><i class="fab fa-linkedin"></i></a></li> - </ul> - </div> - </div> - </div> - </div> - <!-- Top header Close --> <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> - <!-- <h1>Education</h1> --> + <a href="/instructor"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> <!-- <h1>Education</h1> --> </div> <div class="nav-wrap"> <nav class="nav-desktop"> <ul class="menu-list"> - <li><a href="#">Home</a></li> - <li class="menu-parent">Attendance - <ul class="sub-menu"> - <li><a href="#">Child</a></li> - <li><a href="#">Child</a></li> - </ul> - </li> - <li class="menu-parent">Classes - <ul class="sub-menu"> - <li><a href="#">Child</a></li> - <li><a href="#">Child</a></li> - <li class="menu-parent">Child - <ul class="sub-menu"> - <li><a href="">Grand-child</a></li> - <li><a href="">Grand-child</a></li> - </ul> - </li> - </ul> - </li> - <li><a href="">Announcements</a></li> - <li><a href="">Schedule</a></li> - <li><a href="">Profile</a></li> + <li><a href="/instructor">Home</a></li> + <li><a href="/instructor/learning-materials/classes">Lesson Materials</a></li> + <li><a href="/instructor/media/classes">Student Media</a></li> + <li><a href="/instructor/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -86,7 +52,7 @@ <img src="/assets/images/page-banner3.jpg" alt="Image of Bannner"> </div> <div class="container" itemprop="description"> - <h1>Welcome Teachers to EduSync</h1> + <h1>Welcome Instructors to EduSync</h1> <h3>Empowering your teaching experience...</h3> </div> <div id="owl-four-nav" class="owl-nav"></div> @@ -105,13 +71,13 @@ <h3>Empowering your teaching experience...</h3> <div class="container"> <div class="box-wrap" itemprop="about"> <header> - <h1>About EduSync for Teachers</h1> + <h1>About EduSync for Instructors</h1> </header> <p>EduSync provides tools and resources designed to support your teaching methods and enhance classroom engagement.</p> - <h4><a href="https://nkr.sg/"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> - <h4><a href="mailto:nkedusync@gmail.com"><i class="fas fa-envelope"></i> Contact Us</a></h4> - <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> Contact Us</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> </div> <div class="box-wrap"> @@ -143,11 +109,11 @@ <h1>Quick Contact</h1> <div class="container"> <hr class="footer-line"> <ul class="social-list"> - <li><a href=""><i class="fab fa-facebook-square"></i></a></li> - <li><a href=""><i class="fab fa-twitter"></i></a></li> - <li><a href=""><i class="fab fa-skype"></i></a></li> - <li><a href=""><i class="fab fa-youtube"></i></a></li> - <li><a href=""><i class="fab fa-instagram"></i></a></li> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> </ul> <hr class="footer-line"> </div> diff --git a/templates/instructor/lm-classes.html b/templates/instructor/lm-classes.html new file mode 100644 index 0000000..4f94476 --- /dev/null +++ b/templates/instructor/lm-classes.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<html> +<head> + <title>Instructor Profile</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <script> + document.addEventListener("DOMContentLoaded", function() { + fetch('/instructor/classes/get-classes') + .then(response => response.json()) + .then(classes => { + const classList = document.getElementById("class-list"); + classes.forEach(classItem => { + const listItem = document.createElement("li"); + listItem.classList.add("class-item"); // Add class to list item + + const uploadLink = document.createElement("a"); + uploadLink.href = `/instructor/learning-materials/upload?folder_id=${classItem[1]}&class_name=${classItem[0]}`; + uploadLink.target = "_blank"; + uploadLink.textContent = classItem[0]; + + uploadLink.classList.add("class-name"); + listItem.appendChild(uploadLink); + classList.appendChild(listItem); + }); + }) + .catch(error => console.error('Error fetching classes:', error)); + }); + </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + #class-list { + list-style-type: none; /* Remove default bullets */ + padding: 0; /* Remove default padding */ + margin: 0; /* Remove default margin */ + } + + .class-item { + margin-bottom: 10px; /* Adjust spacing between items */ + } + + .class-name { + font-size: 1em; /* Adjust the size as needed */ + font-weight: bold; /* Optional: make the text bold */ + text-decoration: none; /* Optional: remove underline from links */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/instructor"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/instructor">Home</a></li> + <li><a href="/instructor/learning-materials/classes">Lesson Materials</a></li> + <li><a href="/instructor/media/classes">Student Media</a></li> + <li><a href="/instructor/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + <br> + <br> + <div class="container content"> + <h1>Instructor Classes</h1> + <br> + <ul id="class-list"></ul> + <br> + <div class="button-container"> + <button type="button" onclick="window.history.back()">Back</button> + </div> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/instructor/lm-upload.html b/templates/instructor/lm-upload.html new file mode 100644 index 0000000..a9cf4b8 --- /dev/null +++ b/templates/instructor/lm-upload.html @@ -0,0 +1,165 @@ +<!DOCTYPE html> +<html> +<head> + <title>Upload Files</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + hr { + border: none; + border-top: 1px solid #aaa; + margin: 0; + padding: 0; + } + + a:hover { + text-decoration: underline; + } + + /* Style for the file list */ + #file-list { + list-style: none; + padding: 0; + margin: 0; + } + + #file-list li { + margin-bottom: 10px; + } + + #file-list a { + text-decoration: none; + color: #246EB9; /* Blue color for links */ + font-size: 18px; + } + + #file-list a:hover { + text-decoration: underline; + } + + /* Style for the 'Open in Drive' button */ + #folder-link { + display: inline-block; + padding: 10px 20px; + background-color: #246EB9; /* Blue color for button */ + color: white; + text-decoration: none; + border-radius: 5px; + text-align: center; + font-weight: bold; + margin-top: 20px; + } + + #folder-link:hover { + background-color: #FFBE0B; /* Orange color on hover */ + } + </style> + <script> + document.addEventListener("DOMContentLoaded", function() { + const urlParams = new URLSearchParams(window.location.search); + const folderID = urlParams.get('folder_id'); + const folderName = urlParams.get('class_name'); + const googleDriveBaseUrl = "https://drive.google.com/drive/folders/"; + + document.getElementById("folder-link").href = googleDriveBaseUrl + folderID; + document.getElementById("folder-link").target = "_blank"; + + document.getElementById("folder-name").textContent = "Upload to " + folderName; + + fetch(`/api/files?folder_id=${folderID}`) + .then(response => response.json()) + .then(files => { + const fileList = document.getElementById("file-list"); + files.forEach(file => { + const listItem = document.createElement("li"); + const link = document.createElement("a"); + link.href = `https://drive.google.com/file/d/${file.id}`; + link.target = "_blank"; + link.textContent = file.name; + listItem.appendChild(link); + fileList.appendChild(listItem); + }); + }) + .catch(error => console.error('Error fetching files:', error)); + + document.getElementById("upload-form").addEventListener("submit", function(event) { + event.preventDefault(); + const formData = new FormData(this); + formData.append("folderId", folderID); + + fetch('/api/upload', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + alert(`File uploaded successfully. File Name: ${data.name}`); + location.reload(); + }) + .catch(error => console.error('Error uploading file:', error)); + }); + }); + </script> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/instructor"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/instructor">Home</a></li> + <li><a href="/instructor/learning-materials/classes">Lesson Materials</a></li> + <li><a href="/instructor/media/classes">Student Media</a></li> + <li><a href="/instructor/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <br> + <br> + <div class="container content"> + <h1 id="folder-name">Upload to Google Drive</h1> + <br> + <form id="upload-form" enctype="multipart/form-data"> + <label for="fileName"><strong>File Name: </strong></label> + <input type="text" id="fileName" name="fileName" required><br><br> + <label for="file"><strong>Select File: </strong></label> + <input type="file" id="file" name="file" required><br><br> + <button type="submit">Upload</button> + </form> + <br><br><br><br><br> + <h2>Files in Folder</h2> + <ul id="file-list"></ul> + <a id="folder-link" target="_blank"><strong>Open in Drive</strong></a> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> diff --git a/templates/instructor/media-classes.html b/templates/instructor/media-classes.html new file mode 100644 index 0000000..25cbe0b --- /dev/null +++ b/templates/instructor/media-classes.html @@ -0,0 +1,115 @@ +<!DOCTYPE html> +<html> +<head> + <title>Instructor Profile</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <script> + document.addEventListener("DOMContentLoaded", function() { + fetch('/instructor/media/get-classes') + .then(response => response.json()) + .then(classIds => { + const classList = document.getElementById("class-list"); + classIds.forEach(classId => { + const listItem = document.createElement("li"); + listItem.classList.add("class-item"); // Add class to list item + + const uploadLink = document.createElement("a"); + uploadLink.href = `/instructor/media/upload?class_id=${classId}`; + uploadLink.textContent = classId; // Use classId as the display name + + uploadLink.classList.add("class-name"); + listItem.appendChild(uploadLink); + classList.appendChild(listItem); + }); + }) + .catch(error => console.error('Error fetching classes:', error)); + }); + </script> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + + #class-list { + list-style-type: none; /* Remove default bullets */ + padding: 0; /* Remove default padding */ + margin: 0; /* Remove default margin */ + } + + .class-item { + margin-bottom: 10px; /* Adjust spacing between items */ + } + + .class-name { + font-size: 1em; /* Adjust the size as needed */ + font-weight: bold; /* Optional: make the text bold */ + text-decoration: none; /* Optional: remove underline from links */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/instructor"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/instructor">Home</a></li> + <li><a href="/instructor/learning-materials/classes">Lesson Materials</a></li> + <li><a href="/instructor/media/classes">Student Media</a></li> + <li><a href="/instructor/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + <br> + <br> + <div class="container content"> + <h1>Instructor Classes</h1> + <br> + <ul id="class-list"></ul> + <br> + <div class="button-container"> + <button type="button" onclick="window.history.back()">Back</button> + </div> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/instructor/media-upload.html b/templates/instructor/media-upload.html new file mode 100644 index 0000000..25bbbbc --- /dev/null +++ b/templates/instructor/media-upload.html @@ -0,0 +1,215 @@ +<!DOCTYPE html> +<html> +<head> + <title>EduSync</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + table { + width: 75%; + border-collapse: collapse; + } + th, td { + padding: 10px; + border: 1px solid #ddd; + } + th { + background-color: #f4f4f4; + text-align: left; + } + tr:nth-child(even) { + background-color: #f9f9f9; + } + .button-container { + margin-top: 20px; + } + .button-container button { + padding: 10px 20px; + font-size: 16px; + cursor: pointer; + } + .file-upload { + margin: 10px 0; + } + #upload-status { + margin-top: 20px; + font-size: 16px; + color: #333; + } + #upload-status.hidden { + display: none; + } + #page { + max-width: 1200px; /* Adjust this value as needed */ + margin: 0 auto; /* Centers the container horizontally */ + padding: 20px; /* Optional padding for better spacing */ + } + + h1 { + font-size: 3em; /* Adjust as needed */ + } + + p { + font-size: 1.2em; /* Adjust as needed */ + } + + table th, table td { + font-size: 1.5em; /* Adjust as needed */ + } + </style> + <script> + document.addEventListener("DOMContentLoaded", function() { + const classID = new URLSearchParams(window.location.search).get('class_id'); + document.getElementById('heading').textContent = "Upload Student Media for " + classID; + + if (classID) { + fetch(`/instructor/api/media?class_id=${classID}`) + .then(response => response.json()) + .then(data => { + const tableBody = document.getElementById('student-table-body'); + data.forEach(item => { + const row = document.createElement('tr'); + const nameCell = document.createElement('td'); + nameCell.textContent = item.name; + + const folderIDCell = document.createElement('td'); + folderIDCell.textContent = item.folderID; + + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.className = 'file-upload'; + fileInput.dataset.folderId = item.folderID; + fileInput.dataset.studentName = item.name; + + const fileCell = document.createElement('td'); + fileCell.appendChild(fileInput); + + row.appendChild(nameCell); + row.appendChild(fileCell); + row.appendChild(folderIDCell); + tableBody.appendChild(row); + }); + }) + .catch(error => console.error('Error fetching student data:', error)); + } else { + console.error('No class ID provided in the URL'); + } + }); + + function uploadFiles() { + const fileInputs = document.querySelectorAll('.file-upload'); + const formData = new FormData(); + + fileInputs.forEach(fileInput => { + if (fileInput.files.length > 0) { + Array.from(fileInput.files).forEach(file => { + formData.append('files', file); + formData.append('folderIds', fileInput.dataset.folderId); + formData.append('studentNames', fileInput.dataset.studentName); + }); + } + }); + + // Show the uploading message + const uploadStatus = document.getElementById('upload-status'); + uploadStatus.textContent = 'Uploading files...'; + uploadStatus.classList.remove('hidden'); + + fetch('/api/media-upload', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + console.log('Upload success:', data); + // Hide the uploading message and show success message + uploadStatus.textContent = 'Files uploaded successfully!'; + setTimeout(() => { + window.history.back(); + }, 1000); // Delay for 1 second to let the user see the success message + }) + .catch(error => { + console.error('Upload error:', error); + // Hide the uploading message and show error message + uploadStatus.textContent = 'Upload failed. Please try again.'; + }); + } + </script> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/instructor"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> <!-- <h1>Education</h1> --> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/instructor">Home</a></li> + <li><a href="/instructor/learning-materials/classes">Lesson Materials</a></li> + <li><a href="/instructor/media/classes">Student Media</a></li> + <li><a href="/instructor/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + <br> + <br> + <h1 id="heading"></h1> + <br> + <table> + <thead> + <tr> + <th>Student Name</th> + <th>Upload File</th> + <th>Folder ID</th> + </tr> + </thead> + <tbody id="student-table-body"> + <!-- Data will be inserted here by JavaScript --> + </tbody> + </table> + <div class="button-container"> + <button type="button" onclick="uploadFiles()">Upload</button> + <button type="button" onclick="window.history.back()">Back</button> + </div> + <div id="upload-status" class="hidden"></div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/instructor/profile.html b/templates/instructor/profile.html new file mode 100644 index 0000000..86899f5 --- /dev/null +++ b/templates/instructor/profile.html @@ -0,0 +1,86 @@ +<!DOCTYPE html> +<html> +<head> + <title>Instructor Profile</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/instructor"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/instructor">Home</a></li> + <li><a href="/instructor/learning-materials/classes">Lesson Materials</a></li> + <li><a href="/instructor/media/classes">Student Media</a></li> + <li><a href="/instructor/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <br> + <br> + <div class="container content"> + <h1>Instructor Profile</h1> + <br> + <div><label><strong>Name: </strong></label> {{.Name}}</div> + <br> + <div><label><strong>Email: </strong></label> {{.Email}}</div> + <br> + <div><label><strong>Contact Number: </strong></label> {{.ContactNumber}}</div> + <br> + <div><label><strong>Base Pay: </strong></label> {{.BasePay}}</div> + <br> + <div><label><strong>Number of Students: </strong></label> {{.NumberOfStudents}}</div> + <br> + <div class="button-container"> + <button type="button" onclick="window.history.back()">Back</button> + </div> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index b7a55b1..e34f346 100644 --- a/templates/login.html +++ b/templates/login.html @@ -2,21 +2,26 @@ <html> <head> <title>Google Sign In</title> - <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> <!-- load bulma css --> - <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <!-- load fontawesome --> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <style> - body { padding-top:70px; } + body { padding-top: 70px; } + .text-blue { color: #246EB9; } + .btn-orange { background-color: #FFBE0B; color: white; border-color: #FFBE0B; } + .btn-orange:hover, .btn-orange:focus, .btn-orange:active { background-color: #e0a700; border-color: #e0a700; } </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> <div class="container"> - <div class="jumbotron text-center text-success"> + <div class="jumbotron text-center text-blue"> <h1><span class="fa fa-lock"></span> Social Authentication</h1> <p>Login or Register with:</p> - <a href="/auth/google" class="btn btn-danger"><span class="fa fa-google"></span> Sign In with Google</a> + <a href="/auth/google" class="btn btn-orange"><span class="fa fa-google"></span> Sign In with Google</a> + <br><br> + <a href="https://localhost:8080" class="btn btn-secondary"><span class="fa fa-arrow-left"></span> Back</a> </div> </div> </div> </body> -</html> \ No newline at end of file +</html> \ No newline at end of file diff --git a/templates/parent.html b/templates/parent.html new file mode 100644 index 0000000..6fb192d --- /dev/null +++ b/templates/parent.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html> +<head> + <title>EduSync - Parents</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + h2 { + font-size: 2em; /* Adjust size as needed */ + font-weight: bold; + margin-top: 20px; + margin-bottom: 20px; + } + + p { + font-size: 1em; /* Adjust size as needed */ + line-height: 1.6; + margin-bottom: 20px; + } + + /* Content area that takes up remaining space */ + .content { + flex: 1; + padding: 20px; /* Add padding if needed */ + max-width: 1200px; /* Limit the width of content */ + margin: 0 auto; /* Center the content */ + } + + .queries { + margin-top: 40px; + padding: 20px; + background-color: #FFBE0B; + color: #000; + border-radius: 8px; + text-align: center; + } + + .queries a { + color: #246EB9; /* Example color for links */ + text-decoration: none; + font-weight: bold; + } + + a:hover { + color: #4A90E2; /* Lighter shade of blue */ + text-decoration: underline; /* Add underline on hover (optional) */ + } + + /* Slogan styling */ + .slogan { + font-size: 2em; + font-weight: bold; + color: #FFBE0B; /* Example color for the slogan */ + text-align: center; + margin-top: 30px; + margin-bottom: 30px; + font-style: italic; + } + + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <!-- Header --> + <header class="site-header"> + <div class="top-header"> + <div class="container"> + <div class="top-header-left"> + <div class="top-header-block"> + <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> + </div> + <div class="top-header-block"> + <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> + </div> + </div> + <div class="top-header-right"> + <div class="social-block"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + </ul> + </div> + </div> + </div> + </div> + <!-- Top header Close --> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/">Home</a></li> + <li><a href="/admin-role">Admin</a></li> + <li><a href="/parent-role">Parent</a></li> + <li><a href="/student-role">Student</a></li> + <li><a href="/instructor-role">Instructor</a></li> + <li><a href="/login">Login</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <!-- Content --> + <div class="content"> + <!-- Main content --> + <h2>Parent Role</h2> + <p>Parents can use EduSync to access their children's pictures and videos, download them to their own drives, and receive important announcements from the administrators. This helps parents stay engaged and informed about their children's learning journey.</p> + + <!-- Slogan --> + <div class="slogan"> + EduSync: Connect, Learn, Succeed + </div> + + <!-- Queries section --> + <div class="queries"> + <p><strong>If you have any queries, please email us at <a target="_blank" href="mailto:nkedusync@gmail.com">nkedusync@gmail.com</a>. + <br> + <br> + To sign up, please find more information at the <a target="_blank" href="https://nkr.sg/">NK Robotics Website</a>.</strong></p> + </div> + </div> + <br> + + <!-- Footer --> + <footer class="page-footer" itemprop="footer" itemscope itemtype="http://schema.org/WPFooter"> + <div class="footer-first-section"> + <div class="container"> + <div class="box-wrap" itemprop="about"> + <header> + <h1>about</h1> + </header> + <p>EduSync is a great start for remote learning. Created for students, teachers, and parents.</p> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + </div> + + <div class="box-wrap"> + <header> + <h1>User Guides</h1> + </header> + <ul> + <li><a href="#">Students</a></li> + <li><a href="#">Parents</a></li> + <li><a href="#">Teachers</a></li> + </ul> + </div> + + <div class="box-wrap"> + <header> + <h1>quick contact</h1> + </header> + <section class="quick-contact"> + <input type="email" name="email" placeholder="Your Email*"> + <textarea placeholder="Type your message*"></textarea> + <button>send message</button> + </section> + </div> + </div> + </div> + <div class="footer-second-section"> + <div class="container"> + <hr class="footer-line"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> + </ul> + <hr class="footer-line"> + </div> + </div> + <div class="footer-last-section"> + <div class="container"> + <p>© 2024 EduSync. All rights reserved.</p> + </div> + </div> + </footer> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/parent/index.html b/templates/parent/index.html index 01b75fc..e5e4b2b 100644 --- a/templates/parent/index.html +++ b/templates/parent/index.html @@ -7,60 +7,46 @@ <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> + <script> + document.addEventListener("DOMContentLoaded", function() { + fetch('/parent/get-folder-id') + .then(response => response.json()) + .then(data => { + if (data.folder_id) { + const googleDriveBaseUrl = "https://drive.google.com/drive/folders/"; + const folderUrl = googleDriveBaseUrl + data.folder_id; + document.getElementById("media-link").href = folderUrl; + } else { + console.error('Folder ID not found in response'); + } + }) + .catch(error => console.error('Error fetching folder ID:', error)); + }); + </script> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> <header class="site-header"> - <div class="top-header"> - <div class="container"> - <div class="top-header-left"> - <div class="top-header-block"> - <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> - </div> - <div class="top-header-block"> - <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> - </div> - </div> - <div class="top-header-right"> - <div class="social-block"> - <ul class="social-list"> - <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes"><i class="fab fa-viber"></i></a></li> - <li><a href="https://www.facebook.com/NKRobotics/"><i class="fab fa-facebook-square"></i></a></li> - <li><a href="https://www.instagram.com/nk.robotics/"><i class="fab fa-instagram"></i></a></li> - <li><a href="https://sg.linkedin.com/company/nk-robotics"><i class="fab fa-linkedin"></i></a></li> - </ul> - </div> - </div> - </div> - </div> - <!-- Top header Close --> <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + <a href="/parent"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> </div> <div class="nav-wrap"> <nav class="nav-desktop"> <ul class="menu-list"> - <li><a href="#">Home</a></li> - <li class="menu-parent">Queries - <ul class="sub-menu"> - <li><a href="#">FAQ</a></li> - </ul> - </li> - <li class="menu-parent">Student Details - <ul class="sub-menu"> - <li><a href="#">View Details</a></li> - <li class="menu-parent">Class Details - <ul class="sub-menu"> - <li><a href="">Syllabus</a></li> - <li><a href="">Schedule</a></li> - </ul> - </li> - </ul> - </li> - <li><a href="">Announcements</a></li> - <li><a href="">Profile</a></li> + <li><a href="/parent">Home</a></li> + <li><a id="media-link" href="#" target="_blank">Child Media</a></li> + <li><a href="/parent/announcements">Announcements</a></li> + <li><a href="/parent/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -104,9 +90,9 @@ <h1>About EduSync for Parents</h1> </header> <p>EduSync provides tools and resources designed to support your child's learning and help you stay engaged in their education.</p> - <h4><a href="https://nkr.sg/"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> - <h4><a href="mailto:nkedusync@gmail.com"><i class="fas fa-envelope"></i> Contact Us</a></h4> - <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> Contact Us</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> </div> <div class="box-wrap"> @@ -138,11 +124,11 @@ <h1>Quick Contact</h1> <div class="container"> <hr class="footer-line"> <ul class="social-list"> - <li><a href=""><i class="fab fa-facebook-square"></i></a></li> - <li><a href=""><i class="fab fa-twitter"></i></a></li> - <li><a href=""><i class="fab fa-skype"></i></a></li> - <li><a href=""><i class="fab fa-youtube"></i></a></li> - <li><a href=""><i class="fab fa-instagram"></i></a></li> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> </ul> <hr class="footer-line"> </div> diff --git a/templates/parent/profile.html b/templates/parent/profile.html new file mode 100644 index 0000000..36a2aa3 --- /dev/null +++ b/templates/parent/profile.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<html> +<head> + <title>Parent Profile</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/parent"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/parent">Home</a></li> + <li><a href="/parent/media">Child Media</a></li> + <li><a href="/parent/announcements">Announcements</a></li> + <li><a href="/parent/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <br> + <br> + <div class="container content"> + <h1>Parent Profile</h1> + <br> + <div><label><strong>Name: </strong></label> {{.Name}}</div> + <br> + <div><label><strong>Email: </strong></label> {{.Email}}</div> + <br> + <div><label><strong>Contact Number: </strong></label> {{.ContactNumber}}</div> + <br> + <div class="button-container"> + <button type="button" onclick="window.history.back()">Back</button> + </div> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/student.html b/templates/student.html new file mode 100644 index 0000000..0e95745 --- /dev/null +++ b/templates/student.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html> +<head> + <title>EduSync - Students</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + h2 { + font-size: 2em; /* Adjust size as needed */ + font-weight: bold; + margin-top: 20px; + margin-bottom: 20px; + } + + p { + font-size: 1em; /* Adjust size as needed */ + line-height: 1.6; + margin-bottom: 20px; + } + + /* Content area that takes up remaining space */ + .content { + flex: 1; + padding: 20px; /* Add padding if needed */ + max-width: 1200px; /* Limit the width of content */ + margin: 0 auto; /* Center the content */ + } + + .queries { + margin-top: 40px; + padding: 20px; + background-color: #FFBE0B; + color: #000; + border-radius: 8px; + text-align: center; + } + + .queries a { + color: #246EB9; /* Example color for links */ + text-decoration: none; + font-weight: bold; + } + + a:hover { + color: #4A90E2; /* Lighter shade of blue */ + text-decoration: underline; /* Add underline on hover (optional) */ + } + + /* Slogan styling */ + .slogan { + font-size: 2em; + font-weight: bold; + color: #FFBE0B; /* Example color for the slogan */ + text-align: center; + margin-top: 30px; + margin-bottom: 30px; + font-style: italic; + } + + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + </style> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <!-- Header --> + <header class="site-header"> + <div class="top-header"> + <div class="container"> + <div class="top-header-left"> + <div class="top-header-block"> + <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> + </div> + <div class="top-header-block"> + <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> + </div> + </div> + <div class="top-header-right"> + <div class="social-block"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + </ul> + </div> + </div> + </div> + </div> + <!-- Top header Close --> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/">Home</a></li> + <li><a href="/admin-role">Admin</a></li> + <li><a href="/parent-role">Parent</a></li> + <li><a href="/student-role">Student</a></li> + <li><a href="/instructor-role">Instructor</a></li> + <li><a href="/login">Login</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <!-- Content --> + <div class="content"> + <!-- Main content --> + <h2>Student Role</h2> + <p>Students can access learning materials remotely through EduSync and download them to their own drives. This platform allows students to continue their education seamlessly, no matter where they are.</p> + + <!-- Slogan --> + <div class="slogan"> + EduSync: Connect, Learn, Succeed + </div> + + <!-- Queries section --> + <div class="queries"> + <p><strong>If you have any queries, please email us at <a target="_blank" href="mailto:nkedusync@gmail.com">nkedusync@gmail.com</a>. + <br> + <br> + To sign up, please find more information at the <a target="_blank" href="https://nkr.sg/">NK Robotics Website</a>.</strong></p> + </div> + </div> + <br> + + <!-- Footer --> + <footer class="page-footer" itemprop="footer" itemscope itemtype="http://schema.org/WPFooter"> + <div class="footer-first-section"> + <div class="container"> + <div class="box-wrap" itemprop="about"> + <header> + <h1>about</h1> + </header> + <p>EduSync is a great start for remote learning. Created for students, teachers, and parents.</p> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + </div> + + <div class="box-wrap"> + <header> + <h1>User Guides</h1> + </header> + <ul> + <li><a href="#">Students</a></li> + <li><a href="#">Parents</a></li> + <li><a href="#">Teachers</a></li> + </ul> + </div> + + <div class="box-wrap"> + <header> + <h1>quick contact</h1> + </header> + <section class="quick-contact"> + <input type="email" name="email" placeholder="Your Email*"> + <textarea placeholder="Type your message*"></textarea> + <button>send message</button> + </section> + </div> + </div> + </div> + <div class="footer-second-section"> + <div class="container"> + <hr class="footer-line"> + <ul class="social-list"> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> + </ul> + <hr class="footer-line"> + </div> + </div> + <div class="footer-last-section"> + <div class="container"> + <p>© 2024 EduSync. All rights reserved.</p> + </div> + </div> + </footer> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/student/index.html b/templates/student/index.html index f40cfc8..72b9c68 100644 --- a/templates/student/index.html +++ b/templates/student/index.html @@ -8,63 +8,46 @@ <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <script> + document.addEventListener("DOMContentLoaded", function() { + fetch('/student/get-folder-id') + .then(response => response.json()) + .then(data => { + if (data.folder_id) { + const googleDriveBaseUrl = "https://drive.google.com/drive/folders/"; + const folderUrl = googleDriveBaseUrl + data.folder_id; + document.getElementById("materials-link").href = folderUrl; + } else { + console.error('Folder ID not found in response'); + } + }) + .catch(error => console.error('Error fetching folder ID:', error)); + }); + </script> + <style> + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> <header class="site-header"> - <div class="top-header"> - <div class="container"> - <div class="top-header-left"> - <div class="top-header-block"> - <a href="mailto:nkedusync@gmail.com" itemprop="email"><i class="fas fa-envelope"></i> nkedusync@gmail.com</a> - </div> - <div class="top-header-block"> - <a href="https://nkr.sg/" itemprop="globe"><i class="fas fa-globe"></i> Visit Website</a> - </div> - </div> - <div class="top-header-right"> - <div class="social-block"> - <ul class="social-list"> - <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes"><i class="fab fa-viber"></i></a></li> - <li><a href="https://www.facebook.com/NKRobotics/"><i class="fab fa-facebook-square"></i></a></li> - <li><a href="https://www.instagram.com/nk.robotics/"><i class="fab fa-instagram"></i></a></li> - <li><a href="https://sg.linkedin.com/company/nk-robotics"><i class="fab fa-linkedin"></i></a></li> - </ul> - </div> - </div> - </div> - </div> - <!-- Top header Close --> <div class="main-header"> <div class="container"> <div class="logo-wrap" itemprop="logo"> - <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + <a href="/student"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> <!-- <h1>Education</h1> --> </div> <div class="nav-wrap"> <nav class="nav-desktop"> <ul class="menu-list"> - <li><a href="#">Home</a></li> - <li class="menu-parent">Materials - <ul class="sub-menu"> - <li><a href="#">Child</a></li> - <li><a href="#">Child</a></li> - </ul> - </li> - <li class="menu-parent">Feedback - <ul class="sub-menu"> - <li><a href="#">Child</a></li> - <li><a href="#">Child</a></li> - <li class="menu-parent">Child - <ul class="sub-menu"> - <li><a href="">Grand-child</a></li> - <li><a href="">Grand-child</a></li> - </ul> - </li> - </ul> - </li> - <li><a href="">Announcements</a></li> - <li><a href="">{{.Name}}</a></li> + <li><a href="/student">Home</a></li> + <li><a id="materials-link" href="#" target="_blank">Lesson Materials</a></li> + <li><a href="/student/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> </ul> </nav> <div id="bar"> @@ -108,9 +91,9 @@ <h1>About EduSync for Students</h1> </header> <p>EduSync offers a range of resources and tools tailored to enhance your learning experience.</p> - <h4><a href="https://nkr.sg/"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> - <h4><a href="mailto:nkedusync@gmail.com"><i class="fas fa-envelope"></i> Contact Us</a></h4> - <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> + <h4><a href="https://nkr.sg/" target="_blank"><i class="fas fa-globe"></i> Visit EduSync Website</a></h4> + <h4><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i> Contact Us</a></h4> + <h4><a href="https://www.google.com/maps/search/?api=1&query=Singapore" target="_blank"><i class="fas fa-map-marker-alt"></i> Singapore, Singapore</a></h4> </div> <div class="box-wrap"> @@ -142,11 +125,11 @@ <h1>Quick Contact</h1> <div class="container"> <hr class="footer-line"> <ul class="social-list"> - <li><a href=""><i class="fab fa-facebook-square"></i></a></li> - <li><a href=""><i class="fab fa-twitter"></i></a></li> - <li><a href=""><i class="fab fa-skype"></i></a></li> - <li><a href=""><i class="fab fa-youtube"></i></a></li> - <li><a href=""><i class="fab fa-instagram"></i></a></li> + <li><a href="https://www.facebook.com/NKRobotics/" target="_blank"><i class="fab fa-facebook-square"></i></a></li> + <li><a href="https://api.whatsapp.com/send?phone=6580391455&text=Hi,%20I%27d%20like%20to%20enquire%20about%20your%20robotics%20and%20coding%20programmes" target="_blank"><i class="fab fa-viber"></i></a></li> + <li><a href="https://www.instagram.com/nk.robotics/" target="_blank"><i class="fab fa-instagram"></i></a></li> + <li><a href="https://sg.linkedin.com/company/nk-robotics" target="_blank"><i class="fab fa-linkedin"></i></a></li> + <li><a href="mailto:nkedusync@gmail.com" target="_blank"><i class="fas fa-envelope"></i></a></li> </ul> <hr class="footer-line"> </div> diff --git a/templates/student/profile.html b/templates/student/profile.html new file mode 100644 index 0000000..fce33ea --- /dev/null +++ b/templates/student/profile.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html> +<head> + <title>Profile</title> + <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + /* Style the horizontal line */ + hr { + border: none; /* Remove default border */ + border-top: 1px solid #aaa; /* Thin light grey line */ + margin: 0; /* Remove default margin */ + padding: 0; /* Remove default padding */ + } + + a:hover { + text-decoration: underline; /* Add underline on hover (optional) */ + } + </style> + <script> + document.addEventListener("DOMContentLoaded", function() { + fetch('/student/get-folder-id') + .then(response => response.json()) + .then(data => { + if (data.folder_id) { + const googleDriveBaseUrl = "https://drive.google.com/drive/folders/"; + const folderUrl = googleDriveBaseUrl + data.folder_id; + document.getElementById("materials-link").href = folderUrl; + } else { + console.error('Folder ID not found in response'); + } + }) + .catch(error => console.error('Error fetching folder ID:', error)); + }); + </script> +</head> +<body> + <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> + <header class="site-header"> + <div class="main-header"> + <div class="container"> + <div class="logo-wrap" itemprop="logo"> + <a href="/student"> + <img src="/assets/images/site-logo.jpg" alt="Logo Image" style="width: 120px; height: auto;"> + </a> + <!-- <h1>Education</h1> --> + </div> + <div class="nav-wrap"> + <nav class="nav-desktop"> + <ul class="menu-list"> + <li><a href="/student">Home</a></li> + <li><a id="materials-link" href="#" target="_blank">Lesson Materials</a></li> + <li><a href="/student/profile">Profile</a></li> + <li><a href="/logout">Logout</a></li> + </ul> + </nav> + <div id="bar"> + <i class="fas fa-bars"></i> + </div> + <div id="close"> + <i class="fas fa-times"></i> + </div> + </div> + </div> + </div> + </header> + <hr> + <!-- Header Close --> + + <br> + <br> + <div class="container content"> + <h1>Student Profile</h1> + <br> + <div><label><strong>Name: </strong></label> {{.Name}}</div> + <br> + <div><label><strong>Email: </strong></label> {{.Email}}</div> + <br> + <div><label><strong>Contact Number: </strong></label> {{.ContactNumber}}</div> + <br> + <div><label><strong>Age: </strong></label> {{.Age}}</div> + <br> + <div><label><strong>Lesson Credits: </strong></label> {{.LessonCredits}}</div> + <br> + <div><label><strong>Class ID: </strong></label> {{.ClassID}}</div> + <br> + <div class="button-container"> + <button type="button" onclick="window.history.back()">Back</button> + </div> + </div> + </div> + <script type="text/javascript" src="/assets/js/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/assets/js/all.js"></script> + <script type="text/javascript" src="/assets/js/isotope.pkgd.min.js"></script> + <script type="text/javascript" src="/assets/js/owl.carousel.js"></script> + <script type="text/javascript" src="/assets/js/jquery.flexslider.js"></script> + <script type="text/javascript" src="/assets/js/jquery.rateyo.js"></script> + <script type="text/javascript" src="/assets/js/custom.js"></script> +</body> +</html> \ No newline at end of file diff --git a/templates/unregistered.html b/templates/unregistered.html index 09e0a50..9c348ed 100644 --- a/templates/unregistered.html +++ b/templates/unregistered.html @@ -2,12 +2,36 @@ <html> <head> <title>EduSync</title> - <meta name="viewport" content="width=device-width"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> - <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> - <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> - <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/assets/css/owl.carousel.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/inner-page-style.css"> + <link rel="stylesheet" type="text/css" href="/assets/css/style.css"> + <link href="https://fonts.googleapis.com/css?family=Raleway:400,500,600,700" rel="stylesheet"> + <style> + body { + font-family: 'Raleway', sans-serif; + } + .container { + margin-top: 50px; + text-align: center; + } + h2 { + font-size: 3.5em; + color: #246EB9; /* blue */ + } + p { + font-size: 1.5em; + margin-bottom: 30px; + } + a { + font-size: 1.5em; + color: #FFBE0B; /* orange */ + text-decoration: none; + } + a:hover { + color: #e0a700; /* darker orange on hover */ + } + </style> </head> <body> <div id="page" class="site" itemscope itemtype="http://schema.org/LocalBusiness"> @@ -18,4 +42,4 @@ <h2>Account Registration Required</h2> </div> </div> </body> -</html> \ No newline at end of file +</html> \ No newline at end of file