From dca1ad876897ddc8647e6b1032d82af20aa1d297 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Mon, 25 Nov 2024 14:08:37 -0500 Subject: [PATCH 01/37] Added a tab to the dashboard to allow people with permissions to edit which branches are protected --- .../migrations/20240613175300_admin-group.sql | 1 + backend/src/perms.rs | 3 + .../dashboard/AdminDashboard.svelte | 4 +- .../lib/components/dashboard/BranchTab.svelte | 72 +++++++++++++++++++ frontend/src/lib/types.ts | 4 +- 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 frontend/src/lib/components/dashboard/BranchTab.svelte diff --git a/backend/migrations/20240613175300_admin-group.sql b/backend/migrations/20240613175300_admin-group.sql index 8c57a179..c83b1bbf 100644 --- a/backend/migrations/20240613175300_admin-group.sql +++ b/backend/migrations/20240613175300_admin-group.sql @@ -2,3 +2,4 @@ INSERT INTO groups ( name ) VALUES ( "Admin" ); -- this *should* be group 1 INSERT into group_permissions ( group_id, permission ) VALUES ( 1, "ManageContent" ); INSERT into group_permissions ( group_id, permission ) VALUES ( 1, "ManageUsers" ); +INSERT into group_permissions ( group_id, permission ) VALUES ( 1, "ManageBranches" ); diff --git a/backend/src/perms.rs b/backend/src/perms.rs index 7e5d0822..000978eb 100644 --- a/backend/src/perms.rs +++ b/backend/src/perms.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; pub enum Permission { ManageContent, // TODO ManageUsers, + ManageBranches, // TODO: Submit for review } @@ -14,6 +15,7 @@ impl From for String { match value { Permission::ManageContent => "ManageContent", Permission::ManageUsers => "ManageUsers", + Permission::ManageBranches => "ManageBranches" } .to_string() } @@ -25,6 +27,7 @@ impl TryInto for &str { match self { "ManageContent" => Ok(Permission::ManageContent), "ManageUsers" => Ok(Permission::ManageUsers), + "ManageBranches" => Ok(Permission::ManageBranches), _ => Err("Not a valid permission level"), } } diff --git a/frontend/src/lib/components/dashboard/AdminDashboard.svelte b/frontend/src/lib/components/dashboard/AdminDashboard.svelte index cf515ee3..f54cdd67 100644 --- a/frontend/src/lib/components/dashboard/AdminDashboard.svelte +++ b/frontend/src/lib/components/dashboard/AdminDashboard.svelte @@ -2,13 +2,15 @@ import GroupTab from './GroupTab.svelte'; import UserTab from './UserTab.svelte'; import ServerTab from './ServerTab.svelte'; + import BranchTab from './BranchTab.svelte'; export let dialog: HTMLDialogElement; let selectedTab = 0; let tabs = [ { name: 'User Management', id: 0, component: UserTab }, { name: 'Group Management', id: 1, component: GroupTab }, - { name: 'Server Management', id: 2, component: ServerTab } + { name: 'Server Management', id: 2, component: ServerTab }, + { name: 'Protect Branch Management', id: 3, component: BranchTab } ]; // E must be defined as any because for some reason typescript thinks parentElement doesn't exist on e.target diff --git a/frontend/src/lib/components/dashboard/BranchTab.svelte b/frontend/src/lib/components/dashboard/BranchTab.svelte new file mode 100644 index 00000000..7613fa04 --- /dev/null +++ b/frontend/src/lib/components/dashboard/BranchTab.svelte @@ -0,0 +1,72 @@ + + +{#if canAccess} +
+
    + {#each sortedBranches as branch} +
  • + +
  • + {/each} +
+
+{:else} +

You do not have permission to view this page.

+{/if} + + diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 8c46b4fb..b43f54ae 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -8,7 +8,8 @@ */ export enum Permission { ManageUsers = 'ManageUsers', - ManageContent = 'ManageContent' + ManageContent = 'ManageContent', + ManageBranches = 'ManageBranches' } /** @@ -17,6 +18,7 @@ export enum Permission { export const allPermissions: Map = new Map(); allPermissions.set(Permission.ManageContent, 'Manage Content'); allPermissions.set(Permission.ManageUsers, 'Manage Users'); +allPermissions.set(Permission.ManageBranches, 'Manage Branches'); export interface User { id: number; From 7584eedaef890fec9813333a82c857d3e1817533 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 11 Dec 2024 05:12:06 -0500 Subject: [PATCH 02/37] Trying to get issues to work --- backend/src/gh.rs | 66 +++++++++++++++++++ .../src/handlers_prelude/github_handlers.rs | 61 +++++++++++++++++ frontend/src/lib/main.ts | 1 + frontend/src/routes/+page.svelte | 63 +++++++++++++++++- 4 files changed, 190 insertions(+), 1 deletion(-) diff --git a/backend/src/gh.rs b/backend/src/gh.rs index aaa26dd4..e269c871 100644 --- a/backend/src/gh.rs +++ b/backend/src/gh.rs @@ -445,4 +445,70 @@ impl GitHubClient { Ok(branch_details) } + + /// Fetches issues from the GitHub repository. + /// + /// This function retrieves issues from the specified repository using the GitHub API. + /// You can filter issues based on their state and associated labels. + /// + /// # Parameters: + /// - `state`: A string slice representing the state of the issues to fetch (e.g., "open", "closed", "all"). + /// Defaults to "open". + /// - `labels`: A comma-separated string slice representing labels to filter issues by. Defaults to `None`. + /// + /// # Returns: + /// A `Result>`: + /// - `Ok(issues)`: A vector of JSON values representing the issues fetched from the repository. + /// - `Err(e)`: An error message if the request fails or the response cannot be parsed. + /// + /// # Errors: + /// This function may return an error if: + /// - The `repo_url` is not in the expected format and cannot be parsed to derive the repository name. + /// - The request to fetch issues fails due to authentication issues, invalid input, or network problems. + /// - The GitHub API response cannot be parsed as a JSON array. + pub async fn get_issues(&self, state: Option<&str>, labels: Option<&str>) -> Result> { + let repo_name = self.get_repo_name().map_err(|e| { + error!("Failed to get repository name: {:?}", e); + e + })?; + + let state = state.unwrap_or("open"); // Default state + let mut query_params = vec![format!("state={}", state)]; + if let Some(labels) = labels { + query_params.push(format!("labels={}", labels)); + } + let query_string = format!("?{}", query_params.join("&")); + + let url = format!("{}/repos/{}/issues{}", GITHUB_API_URL, repo_name, query_string); + debug!("Request URL: {}", url); + + let response = self + .client + .get(&url) + .bearer_auth(&self.token) + .header("Accept", "application/vnd.github+json") + .header("User-Agent", "Hyde") + .timeout(std::time::Duration::from_secs(10)) + .send() + .await?; + + if let Some(rate_limit_remaining) = response.headers().get("X-RateLimit-Remaining") { + debug!("GitHub API rate limit remaining: {}", rate_limit_remaining.to_str().unwrap_or("Unknown")); + } + + if !response.status().is_success() { + let status = response.status(); + let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string()); + error!("GitHub API request failed with status {}: {}", status, error_text); + bail!("GitHub API request failed ({}): {}", status, error_text); + } + + let issues: Vec = response.json().await.map_err(|e| { + error!("Failed to parse GitHub response JSON: {:?}", e); + e + })?; + + Ok(issues) + } + } diff --git a/backend/src/handlers_prelude/github_handlers.rs b/backend/src/handlers_prelude/github_handlers.rs index 322ccb96..5f2448ea 100644 --- a/backend/src/handlers_prelude/github_handlers.rs +++ b/backend/src/handlers_prelude/github_handlers.rs @@ -6,6 +6,7 @@ use axum::{ use axum::routing::{get, post, put}; use tracing::{error, info}; use serde::{Serialize, Deserialize}; +use serde_json::Value; use crate::gh::GitHubClient; use crate::handlers_prelude::eyre_to_axum_err; use crate::AppState; @@ -46,6 +47,19 @@ pub struct CreatePRRequest { pub description: String, } +#[derive(Serialize, Debug)] +pub struct IssuesData { + pub issues: Vec, +} + +#[derive(Serialize)] +pub struct Issue { + pub id: u64, + pub title: String, + pub state: String, + pub labels: Vec, +} + /// Retrieves the GitHub access token from the application state. async fn get_github_token(state: &AppState) -> Result { state.gh_credentials.get(&state.reqwest_client, &state.config.oauth.github.client_id).await.map_err(|err| { @@ -237,6 +251,52 @@ pub async fn get_current_branch_handler(State(state): State) -> Result } } +/// Handler to fetch issues from a GitHub repository. +pub async fn get_issues_handler( + State(state): State, + Path(state_param): Path>, // Only take the state parameter +) -> Result<(StatusCode, Json>), (StatusCode, String)> { + info!("Received request to fetch issues"); + + // Logging state for debugging + info!("State param: {:?}", state_param); + + let state_param = state_param.as_deref().unwrap_or("open"); + + // Get the GitHubClient instance + let github_client = GitHubClient::new( + state.config.files.repo_url.clone(), + state.reqwest_client.clone(), + get_github_token(&state).await.map_err(|err| { + let error_message = format!("Failed to get GitHub token: {:?}", err); + error!("{}", error_message); // Log the error here + (StatusCode::INTERNAL_SERVER_ERROR, error_message) + })?, + ); + + // Fetch issues using the GitHub client + match github_client + .get_issues(Some(state_param), None) + .await + { + Ok(issues) => { + info!("Issues fetched successfully."); + let response = ApiResponse { + status: "success".to_string(), + message: "Issues fetched successfully.".to_string(), + data: Some(IssuesData { issues }), + }; + Ok((StatusCode::OK, Json(response))) + } + Err(err) => { + // Log and return an error + let error_message = format!("Failed to fetch issues: {:?}", err); + error!("{}", error_message); // Log the error here + Err((StatusCode::INTERNAL_SERVER_ERROR, error_message)) + } + } +} + /// Route definitions for GitHub operations pub async fn github_routes() -> Router { Router::new() @@ -245,4 +305,5 @@ pub async fn github_routes() -> Router { .route("/checkout/branches/:branch_name", put(checkout_or_create_branch_handler)) .route("/pull/:branch", post(pull_handler)) .route("/current-branch", get(get_current_branch_handler)) + .route("/issues/:state", get(get_issues_handler)) } diff --git a/frontend/src/lib/main.ts b/frontend/src/lib/main.ts index c9285924..e6a1f945 100644 --- a/frontend/src/lib/main.ts +++ b/frontend/src/lib/main.ts @@ -24,6 +24,7 @@ export const me: Writable = writable({ export const branchName: Writable = writable('Set Branch'); // Default branch name export const allBranches = writable([]); export const editorText = writable(''); +export const openIssues = writable([]); /** * The filesystem tree for the document folder diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 04956140..10bd844d 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -16,7 +16,8 @@ editorText, apiAddress, assetTree, - allBranches + allBranches, + openIssues } from '$lib/main'; import { onMount } from 'svelte'; import { dev } from '$app/environment'; @@ -40,6 +41,10 @@ documentTree.set(fetchedRootNode); // Update the store with the fetched data }); + onMount(() => { + getOpenIssues(); + }); + let showChangeDialogue: boolean; let showLoadingIcon: boolean; let showSettingsMenu: boolean; @@ -173,6 +178,62 @@ }); }); + async function getOpenIssues() { + const state = "open"; + const labels = ""; + const url = `${apiAddress}/api/issues/${state}${labels ? `?labels=${labels}` : ''}`; + console.log(url) + + try { + // Fetch the data from the API + const response = await fetch(url, { + method: 'GET', + credentials: 'include', + }); + + // Check if the response is successful (status code 2xx) + if (!response.ok) { + const errorMessage = `Failed to fetch open issues. (Code ${response.status}: "${response.statusText}")`; + addToast({ + message: errorMessage, + type: ToastType.Error, + dismissible: true, + }); + return; + } + + // Parse the response as JSON + const responseData = await response.json(); + + // Validate the response structure + if (responseData.status === "success" && Array.isArray(responseData.data?.issues)) { + // Update the store with the issues data + openIssues.set(responseData.data.issues); + console.log(responseData.data.issues); // Optional: for debugging purposes + } else { + // Handle unexpected response structure + const errorMessage = `Unexpected response structure: ${JSON.stringify(responseData)}`; + addToast({ + message: errorMessage, + type: ToastType.Error, + dismissible: true, + }); + } + } catch (error: unknown) { + // Handle fetch or network errors + let errorMessage = 'An unknown error occurred.'; + if (error instanceof Error) { + errorMessage = `An error occurred while processing the response: ${error.message}`; + } + + addToast({ + message: errorMessage, + type: ToastType.Error, + dismissible: true, + }); + } + } + let createPullRequestHandler = async (): Promise => { const title = `Pull request for ${$currentFile}`; const description = `This pull request contains changes made by ${$me.username}.`; From 1b53bc05a60952c1b278aa55aa53d60b3da88521 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 11 Dec 2024 06:35:43 -0500 Subject: [PATCH 03/37] Getting issue with the help of Node --- backend/src/handlers_prelude/github_handlers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/handlers_prelude/github_handlers.rs b/backend/src/handlers_prelude/github_handlers.rs index 5f2448ea..d2012dee 100644 --- a/backend/src/handlers_prelude/github_handlers.rs +++ b/backend/src/handlers_prelude/github_handlers.rs @@ -254,14 +254,14 @@ pub async fn get_current_branch_handler(State(state): State) -> Result /// Handler to fetch issues from a GitHub repository. pub async fn get_issues_handler( State(state): State, - Path(state_param): Path>, // Only take the state parameter + Path(state_param): Path, ) -> Result<(StatusCode, Json>), (StatusCode, String)> { info!("Received request to fetch issues"); // Logging state for debugging info!("State param: {:?}", state_param); - let state_param = state_param.as_deref().unwrap_or("open"); + let state_param = state_param.as_str(); // Get the GitHubClient instance let github_client = GitHubClient::new( From 2adbe8ba1dec23bb40dbaf04b59830b09053cb34 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 11 Dec 2024 08:35:31 -0500 Subject: [PATCH 04/37] Move pull request button to top bar --- .../src/lib/components/BranchButton.svelte | 2 +- .../components/editors/DocumentEditor.svelte | 34 ------ .../src/lib/components/topbar/TopBar.svelte | 41 ++++--- frontend/src/routes/+page.svelte | 115 +----------------- 4 files changed, 29 insertions(+), 163 deletions(-) diff --git a/frontend/src/lib/components/BranchButton.svelte b/frontend/src/lib/components/BranchButton.svelte index ba8df58f..d90f776f 100644 --- a/frontend/src/lib/components/BranchButton.svelte +++ b/frontend/src/lib/components/BranchButton.svelte @@ -358,7 +358,7 @@ align-items: center; justify-content: center; background-color: transparent; - color: var(--background-5); + color: var(--foreground-3); border-radius: 0.3rem; padding: 0.5rem 1rem; cursor: pointer; diff --git a/frontend/src/lib/components/editors/DocumentEditor.svelte b/frontend/src/lib/components/editors/DocumentEditor.svelte index 4d18b4ef..35e43d92 100644 --- a/frontend/src/lib/components/editors/DocumentEditor.svelte +++ b/frontend/src/lib/components/editors/DocumentEditor.svelte @@ -62,7 +62,6 @@ } export let saveChangesHandler: (commitMessage: string) => Promise; - export let createPullRequestHandler: () => Promise; async function cancelChangesHandler() { if ($editorText !== get(currentFile)) { @@ -137,24 +136,6 @@ - -
@@ -287,21 +268,6 @@ outline-width: 0; } - .pull-request { - display: flex; - align-items: center; - justify-content: center; - padding: 0.125rem 0.25rem; - } - - .pull-request span { - margin-left: 0.25rem; - } - - .pull-request svg { - margin-top: 0.125rem; - } - .preview-pane { /* sizing and spacing */ float: left; diff --git a/frontend/src/lib/components/topbar/TopBar.svelte b/frontend/src/lib/components/topbar/TopBar.svelte index 44448d79..622bc20c 100644 --- a/frontend/src/lib/components/topbar/TopBar.svelte +++ b/frontend/src/lib/components/topbar/TopBar.svelte @@ -3,6 +3,7 @@ import { onMount } from 'svelte'; import { createEventDispatcher } from 'svelte'; import BranchButton from '../BranchButton.svelte'; + import PullRequest from './PullRequest.svelte'; let username: string = ''; let profilePictureUrl = ''; @@ -31,21 +32,24 @@
-
-

{username}

- - Profile +
+ +
+

{username}

+ + Profile +
@@ -63,6 +67,13 @@ margin-right: 0.6rem; } + .right-items { + display: flex; + justify-content: flex-end; + align-items: center; + margin-left: auto; /* Pushes the right items to the right */ + } + .settings { cursor: pointer; display: flex; diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 10bd844d..86de0ee6 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -16,8 +16,7 @@ editorText, apiAddress, assetTree, - allBranches, - openIssues + allBranches } from '$lib/main'; import { onMount } from 'svelte'; import { dev } from '$app/environment'; @@ -41,10 +40,6 @@ documentTree.set(fetchedRootNode); // Update the store with the fetched data }); - onMount(() => { - getOpenIssues(); - }); - let showChangeDialogue: boolean; let showLoadingIcon: boolean; let showSettingsMenu: boolean; @@ -178,112 +173,6 @@ }); }); - async function getOpenIssues() { - const state = "open"; - const labels = ""; - const url = `${apiAddress}/api/issues/${state}${labels ? `?labels=${labels}` : ''}`; - console.log(url) - - try { - // Fetch the data from the API - const response = await fetch(url, { - method: 'GET', - credentials: 'include', - }); - - // Check if the response is successful (status code 2xx) - if (!response.ok) { - const errorMessage = `Failed to fetch open issues. (Code ${response.status}: "${response.statusText}")`; - addToast({ - message: errorMessage, - type: ToastType.Error, - dismissible: true, - }); - return; - } - - // Parse the response as JSON - const responseData = await response.json(); - - // Validate the response structure - if (responseData.status === "success" && Array.isArray(responseData.data?.issues)) { - // Update the store with the issues data - openIssues.set(responseData.data.issues); - console.log(responseData.data.issues); // Optional: for debugging purposes - } else { - // Handle unexpected response structure - const errorMessage = `Unexpected response structure: ${JSON.stringify(responseData)}`; - addToast({ - message: errorMessage, - type: ToastType.Error, - dismissible: true, - }); - } - } catch (error: unknown) { - // Handle fetch or network errors - let errorMessage = 'An unknown error occurred.'; - if (error instanceof Error) { - errorMessage = `An error occurred while processing the response: ${error.message}`; - } - - addToast({ - message: errorMessage, - type: ToastType.Error, - dismissible: true, - }); - } - } - - let createPullRequestHandler = async (): Promise => { - const title = `Pull request for ${$currentFile}`; - const description = `This pull request contains changes made by ${$me.username}.`; - const headBranch = $branchName; - - const response = await fetch(`${apiAddress}/api/pulls`, { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - head_branch: headBranch, - base_branch: 'master', - title: title, - description: description - }) - }); - - // Handle the response - if (!response.ok) { - const errorMessage = `Failed to create pull request (Code ${response.status}: "${response.statusText}")`; - addToast({ - message: `Error: ${errorMessage}`, - type: ToastType.Error, - dismissible: true - }); - return; // Exit the function early on error - } - - // Parse the JSON response to get the pull request URL - const jsonResponse = await response.json(); - const pullRequestUrl = jsonResponse.data?.pull_request_url; // Adjusted based on API response - - if (pullRequestUrl) { - // If successful, show success toast with the URL - addToast({ - message: `Pull request created successfully. View it [here](${pullRequestUrl}).`, - type: ToastType.Success, - dismissible: true - }); - } else { - // Handle the case where the URL is not present (if needed) - addToast({ - message: 'Pull request created successfully, but the URL is not available.', - type: ToastType.Warning, - dismissible: true - }); - } - };
@@ -328,7 +217,7 @@ /> {#if mode === SelectedMode.Documents} {#if showEditor && $currentFile !== ''} - + {:else}

From a6721903881953d7d01d70b5cd6aa60d6144e774 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Tue, 17 Dec 2024 08:32:17 -0500 Subject: [PATCH 05/37] Added issues and the ability to link them to a pull request --- .vscode/launch.json | 16 + backend/src/gh.rs | 17 +- .../src/handlers_prelude/github_handlers.rs | 2 + .../lib/components/topbar/PullRequest.svelte | 313 ++++++++++++++++++ frontend/src/lib/main.ts | 4 +- frontend/src/lib/types.ts | 9 + 6 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 frontend/src/lib/components/topbar/PullRequest.svelte diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..10efcb2f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/backend/src/gh.rs b/backend/src/gh.rs index e269c871..6be6becf 100644 --- a/backend/src/gh.rs +++ b/backend/src/gh.rs @@ -250,16 +250,25 @@ impl GitHubClient { base_branch: &str, pr_title: &str, pr_description: &str, + issue_numbers: Option>, ) -> Result { // Parse the repository name from self.repo_url let repo_name = self.get_repo_name()?; - // Prepare the JSON body for the pull request - let pr_body = json!({ + let mut pr_body = pr_description.to_string(); + + // If issue numbers are provided, add them to the body + if let Some(issues) = issue_numbers { + for issue in issues { + pr_body.push_str(&format!("\n\nCloses #{}", issue)); // Add "Closes #" for each issue + } + } + + let pr_body_json = json!({ "title": pr_title, "head": head_branch, "base": base_branch, - "body": pr_description, + "body": pr_body, }); debug!("Creating pull request to {}/repos/{}/pulls", GITHUB_API_URL, repo_name); @@ -270,7 +279,7 @@ impl GitHubClient { .post(format!("{}/repos/{}/pulls", GITHUB_API_URL, repo_name)) .bearer_auth(&self.token) .header("User-Agent", "Hyde") - .json(&pr_body) + .json(&pr_body_json) .send() .await?; diff --git a/backend/src/handlers_prelude/github_handlers.rs b/backend/src/handlers_prelude/github_handlers.rs index d2012dee..e57db585 100644 --- a/backend/src/handlers_prelude/github_handlers.rs +++ b/backend/src/handlers_prelude/github_handlers.rs @@ -45,6 +45,7 @@ pub struct CreatePRRequest { pub base_branch: String, pub title: String, pub description: String, + pub issue_numbers: Option>, } #[derive(Serialize, Debug)] @@ -148,6 +149,7 @@ pub async fn create_pull_request_handler( &payload.base_branch, &payload.title, &payload.description, + payload.issue_numbers ) .await { diff --git a/frontend/src/lib/components/topbar/PullRequest.svelte b/frontend/src/lib/components/topbar/PullRequest.svelte new file mode 100644 index 00000000..79682958 --- /dev/null +++ b/frontend/src/lib/components/topbar/PullRequest.svelte @@ -0,0 +1,313 @@ + + + +

+ + + + + {#if showModal} + + {/if} +
+ + \ No newline at end of file diff --git a/frontend/src/lib/main.ts b/frontend/src/lib/main.ts index e6a1f945..5f0e9974 100644 --- a/frontend/src/lib/main.ts +++ b/frontend/src/lib/main.ts @@ -1,5 +1,5 @@ import { writable, type Writable } from 'svelte/store'; -import type { User, INode, Branch } from './types'; +import type { User, INode, Branch, Issue } from './types'; export const currentFile = writable(''); import { dev } from '$app/environment'; @@ -25,6 +25,8 @@ export const branchName: Writable = writable('Set Branch'); // Default b export const allBranches = writable([]); export const editorText = writable(''); export const openIssues = writable([]); +export const selectedIssues = writable([]); + /** * The filesystem tree for the document folder diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index b43f54ae..664de9b2 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -42,3 +42,12 @@ export interface Branch { name: string; isProtected: boolean; } + +export interface Issue { + id: number; + number: number; + title: string; + state: string; + labels: string[]; + pull_request?: {url: string;}; +} From 4447d9fa0ac52f4d6ec5476b6b879e7df75e5e5e Mon Sep 17 00:00:00 2001 From: TheKrol Date: Tue, 17 Dec 2024 08:53:05 -0500 Subject: [PATCH 06/37] Added a description --- .../src/lib/components/topbar/PullRequest.svelte | 16 ++++++++++++++-- frontend/src/lib/types.ts | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/topbar/PullRequest.svelte b/frontend/src/lib/components/topbar/PullRequest.svelte index 79682958..4a98f865 100644 --- a/frontend/src/lib/components/topbar/PullRequest.svelte +++ b/frontend/src/lib/components/topbar/PullRequest.svelte @@ -8,6 +8,7 @@ let showModal = false; let selectedIssueDetails: Issue | null = null; + let description = ''; function openModal() { showModal = true; @@ -17,6 +18,8 @@ function closeModal() { selectedIssues.set([]); + console.log(description) + description = ''; showModal = false; selectedIssueDetails = null; @@ -99,7 +102,7 @@ let createPullRequestHandler = async (): Promise => { const title = `Pull request for ${$currentFile}`; - const description = `This pull request contains changes made by ${$me.username}.`; + const pr_description = `This pull request contains changes made by ${$me.username}.\n ${description}`; const headBranch = $branchName; // Get selected issues from the store @@ -116,7 +119,7 @@ head_branch: headBranch, base_branch: 'master', title: title, - description: description, + description: pr_description, issue_numbers: selectedIssueNumbers }) }); @@ -215,6 +218,7 @@ {/each}

Selected Issues: {$selectedIssues.length}

+
{/if} + \ No newline at end of file diff --git a/frontend/src/lib/components/topbar/PullRequest.svelte b/frontend/src/lib/components/topbar/PullRequest.svelte index 4012ac7d..cae003c2 100644 --- a/frontend/src/lib/components/topbar/PullRequest.svelte +++ b/frontend/src/lib/components/topbar/PullRequest.svelte @@ -2,7 +2,7 @@
-

Current Base Branch: {$baseBranch}

- - - +

Current Base Branch: {$baseBranch}

+ + +
\ No newline at end of file + .branch-dropdown option { + padding: 0.625rem; + } + diff --git a/frontend/src/lib/components/dashboard/BranchTab.svelte b/frontend/src/lib/components/dashboard/BranchTab.svelte index 7613fa04..8d068671 100644 --- a/frontend/src/lib/components/dashboard/BranchTab.svelte +++ b/frontend/src/lib/components/dashboard/BranchTab.svelte @@ -1,72 +1,72 @@ {#if canAccess} -
-
    - {#each sortedBranches as branch} -
  • - -
  • - {/each} -
-
+
+
    + {#each sortedBranches as branch} +
  • + +
  • + {/each} +
+
{:else} -

You do not have permission to view this page.

+

You do not have permission to view this page.

{/if} diff --git a/frontend/src/lib/components/topbar/PullRequest.svelte b/frontend/src/lib/components/topbar/PullRequest.svelte index 1c83805f..0882ce9f 100644 --- a/frontend/src/lib/components/topbar/PullRequest.svelte +++ b/frontend/src/lib/components/topbar/PullRequest.svelte @@ -1,127 +1,138 @@
- + - - {#if showModal} - - {/if} + + {#if showModal} + + {/if}
\ No newline at end of file + .pull-request span { + margin-left: 0.25rem; + } + + .pull-request svg { + margin-top: 0.125rem; + } + + .modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + outline: none; + } + + .modal-content { + background: var(--background-3); + padding: 1.5rem; + border-radius: 8px; + position: relative; + width: 60%; + height: 60%; + margin-left: 1rem; + overflow-y: scroll; + } + + .close-btn { + position: absolute; + top: 0.5rem; + right: 0.5rem; + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: var(--foreground-3); + transition: color 0.2s ease-in-out; + } + + .issues { + display: flex; + align-items: flex-start; + gap: 0.5rem; + margin-left: -2.75rem; + } + + .issue-container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + + .issues input[type='checkbox'] { + width: 1.25rem; + height: 1.25rem; + cursor: pointer; + accent-color: var(--background-1); + } + + .issues label { + display: flex; + align-items: center; + cursor: pointer; + } + + .issue-title { + background: none; + border: none; + font-size: 1rem; + font-weight: 500; + color: var(--foreground-1); + cursor: pointer; + transition: color 0.2s ease-in-out; + } + + .issue-title a { + display: inline-flex; + align-items: center; + color: var(--foreground-1); + text-decoration: none; + margin-left: 0.5rem; + } + + .issue-svg { + width: 0.9rem; + height: 0.9rem; + align-items: center; + fill: var(--foreground-1); + transition: fill 0.2s ease-in-out; + position: relative; + } + + .issue-svg:hover { + background-color: var(--background-1); + } + + .issue-body { + font-size: 0.75rem; + color: var(--foreground-2); + line-height: 1.5; + } + + .show-more-button { + font-size: 0.875rem; + padding: 0.25rem 0.5rem; + margin-top: 0.5rem; + background-color: transparent; + border: 1px solid var(--foreground-1); + border-radius: 0.375rem; + color: var(--foreground-1); + cursor: pointer; + transition: + background-color 0.2s ease-in-out, + color 0.2s ease-in-out; + } + + .show-more-button:hover { + background-color: var(--background-2); + color: var(--primary); + } + + .submit-pr-btn { + margin-top: 1rem; + padding: 0.5rem 1rem; + background-color: var(--background-4); + color: var(--foreground-1); + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + width: 100%; + } + + .submit-pr-btn:hover { + background-color: var(--foreground-5); + } + + .description-textarea { + width: 100%; + margin-top: 1rem; + padding: 0.5rem; + resize: vertical; + border: 0.5rem; + background: var(--background-3); + color: var(--background-0); + } + + ul { + list-style: none; + } + + li { + margin-bottom: 0.5rem; + } + diff --git a/frontend/src/lib/main.ts b/frontend/src/lib/main.ts index 39e3b036..7d86268a 100644 --- a/frontend/src/lib/main.ts +++ b/frontend/src/lib/main.ts @@ -29,7 +29,6 @@ export const openIssues = writable([]); export const selectedIssues = writable([]); export const openPullRequests = writable([]); - /** * The filesystem tree for the document folder */ diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 8a2620c6..2fe9f620 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -44,13 +44,13 @@ export interface Branch { } export interface Issue { - id: number; + id: number; number: number; - title: string; - state: string; - labels: string[]; + title: string; + state: string; + labels: string[]; body: string; - pull_request?: {url: string;}; + pull_request?: { url: string }; html_url: string; url: string; } diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 86de0ee6..83af108a 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -172,7 +172,6 @@ } }); }); -
@@ -217,7 +216,7 @@ /> {#if mode === SelectedMode.Documents} {#if showEditor && $currentFile !== ''} - + {:else}

From 66bf4f38e12c19e9ac190f28a8d3726553845694 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Mon, 23 Dec 2024 16:08:07 -0500 Subject: [PATCH 21/37] Formatting update --- frontend/src/lib/components/dashboard/BaseBranch.svelte | 2 +- frontend/src/lib/components/topbar/PullRequest.svelte | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/components/dashboard/BaseBranch.svelte b/frontend/src/lib/components/dashboard/BaseBranch.svelte index 43a19f83..96c8dd8f 100644 --- a/frontend/src/lib/components/dashboard/BaseBranch.svelte +++ b/frontend/src/lib/components/dashboard/BaseBranch.svelte @@ -7,7 +7,7 @@ diff --git a/frontend/src/lib/components/topbar/PullRequest.svelte b/frontend/src/lib/components/topbar/PullRequest.svelte index 0882ce9f..c3bf3004 100644 --- a/frontend/src/lib/components/topbar/PullRequest.svelte +++ b/frontend/src/lib/components/topbar/PullRequest.svelte @@ -1,6 +1,6 @@ {#if canAccess}

    + Enable Branch Protection {#each sortedBranches as branch}
  • -
  • {/each}
@@ -27,17 +30,25 @@ diff --git a/frontend/src/lib/components/dashboard/ServerTab.svelte b/frontend/src/lib/components/dashboard/ServerTab.svelte index dc973dc4..328e0964 100644 --- a/frontend/src/lib/components/dashboard/ServerTab.svelte +++ b/frontend/src/lib/components/dashboard/ServerTab.svelte @@ -1,14 +1,12 @@
-
-

Actions

-
-
+ Actions
-
-

Metrics

-
-
+ Metrics TODO
@@ -72,26 +67,11 @@ color: var(--foreground-3); } - .header p { - margin-top: 0.3rem; - margin-bottom: 0; - padding-left: 0.3rem; - font-size: 0.7rem; - color: var(--foreground-3); - } - - .header hr { - margin: 0.2rem; - border-color: var(--foreground-5); - } - button { display: flex; align-items: center; padding-left: 1rem; width: 100%; - height: 2rem; - border-radius: 0.5rem; background-color: transparent; border: none; } diff --git a/frontend/src/lib/components/dashboard/UserTab.svelte b/frontend/src/lib/components/dashboard/UserTab.svelte index cccbad9d..5fa3a354 100644 --- a/frontend/src/lib/components/dashboard/UserTab.svelte +++ b/frontend/src/lib/components/dashboard/UserTab.svelte @@ -4,6 +4,7 @@ import { addToast, ToastType } from '$lib/toast'; import type { Group, User } from '$lib/types'; import { onMount } from 'svelte'; + import SectionHeader from '../elements/SectionHeader.svelte'; // const allGroups = [{'Admin', 'Group 1', 'Group 2', 'Group 3']; let allGroups: Group[] = [ @@ -70,7 +71,7 @@
    -
  • Users
  • + Users {#each users.entries() as [index, user]}
    - -
  • Groups
  • + Groups {#each allGroups as group}
-
-

Settings

-
-
+ Settings {#if showAdminDashboard}
@@ -125,19 +123,6 @@ height: 100vh; } - .settings-header p { - margin-top: 0.3rem; - margin-bottom: 0; - padding-left: 0.3rem; - font-size: 0.7rem; - color: var(--foreground-3); - } - - .settings-header hr { - margin: 0.2rem; - border-color: var(--foreground-5); - } - button { display: flex; align-items: center; diff --git a/frontend/static/css/theme.css b/frontend/static/css/theme.css index 0ef38afd..965ee64a 100644 --- a/frontend/static/css/theme.css +++ b/frontend/static/css/theme.css @@ -29,6 +29,8 @@ --foreground-3: #bababa; --foreground-4: #aaaaaa; --foreground-5: #8a8a8a; + + --accent-0: #9347b4; } :root[data-theme='light'] { From 47b46f285144180da9aed5f28ba60f9d41fac53d Mon Sep 17 00:00:00 2001 From: arc Date: Mon, 30 Dec 2024 12:39:25 -0700 Subject: [PATCH 32/37] feat(frontend): more work on branch management --- .../lib/components/dashboard/BranchTab.svelte | 67 ++++++++++++------- .../components/elements/SectionHeader.svelte | 35 ++++++---- .../components/elements/ToggleSwitch.svelte | 19 +++--- frontend/src/lib/main.ts | 4 ++ 4 files changed, 78 insertions(+), 47 deletions(-) diff --git a/frontend/src/lib/components/dashboard/BranchTab.svelte b/frontend/src/lib/components/dashboard/BranchTab.svelte index 5242f96f..928c7e27 100644 --- a/frontend/src/lib/components/dashboard/BranchTab.svelte +++ b/frontend/src/lib/components/dashboard/BranchTab.svelte @@ -1,9 +1,9 @@
- {label} +
    - Enable Branch Protection For: + Enable Branch Protection For: {#each sortedBranches as branch}
  • - {branch.name} + {branch.name}
  • {/each}
@@ -46,7 +48,7 @@ max-height: 100%; overflow-y: scroll; justify-items: center; - padding: 0.5rem 1rem 0.5rem 1rem; + padding: 0.5rem 1rem; } .container ul { @@ -66,7 +68,7 @@ flex-direction: column; margin: 0.3rem; } - + .base-branch-container label { margin-left: 0.1rem; font-size: 0.8rem; @@ -78,7 +80,6 @@ color: var(--foreground-4); font-size: 0.6rem; margin: 0.1rem; - margin-left: 0.3rem; } diff --git a/frontend/src/lib/components/dashboard/GroupTab.svelte b/frontend/src/lib/components/dashboard/GroupTab.svelte index bbceb6ed..8786ccce 100644 --- a/frontend/src/lib/components/dashboard/GroupTab.svelte +++ b/frontend/src/lib/components/dashboard/GroupTab.svelte @@ -52,7 +52,7 @@
    - Groups + Groups {#each groups.entries() as [index, group]} {#if group.name !== 'Admin'} diff --git a/frontend/src/lib/components/dashboard/ServerTab.svelte b/frontend/src/lib/components/dashboard/ServerTab.svelte index 328e0964..b2b4d57b 100644 --- a/frontend/src/lib/components/dashboard/ServerTab.svelte +++ b/frontend/src/lib/components/dashboard/ServerTab.svelte @@ -6,7 +6,7 @@
    - Actions + Actions
    - Metrics + Metrics TODO
    diff --git a/frontend/src/lib/components/dashboard/UserTab.svelte b/frontend/src/lib/components/dashboard/UserTab.svelte index 5fa3a354..4fd0e35e 100644 --- a/frontend/src/lib/components/dashboard/UserTab.svelte +++ b/frontend/src/lib/components/dashboard/UserTab.svelte @@ -71,7 +71,7 @@
      - Users + Users {#each users.entries() as [index, user]}
      - Groups + Groups {#each allGroups as group}
    • diff --git a/frontend/src/lib/components/elements/ToggleSwitch.svelte b/frontend/src/lib/components/elements/ToggleSwitch.svelte index 92e9ab53..5cfeae0d 100644 --- a/frontend/src/lib/components/elements/ToggleSwitch.svelte +++ b/frontend/src/lib/components/elements/ToggleSwitch.svelte @@ -3,7 +3,7 @@