improve navigation within workspaces
This commit is contained in:
parent
cf4c07f5b8
commit
ab5f778cc4
10 changed files with 241 additions and 43 deletions
|
|
@ -13,7 +13,7 @@ use uuid::Uuid;
|
|||
use validator::Validate;
|
||||
|
||||
use crate::{
|
||||
app::{App, AppDbConn},
|
||||
app::App,
|
||||
errors::AppError,
|
||||
extractors::ValidatedForm,
|
||||
navigator::{Navigator, NavigatorPage as _},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ mod add_service_credential_handler;
|
|||
mod add_table_handler;
|
||||
mod nav_handler;
|
||||
mod service_credentials_handler;
|
||||
mod settings_handler;
|
||||
mod update_name_handler;
|
||||
mod update_service_cred_permissions_handler;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
|
|
@ -37,6 +39,11 @@ pub(super) fn new_router() -> Router<App> {
|
|||
},
|
||||
),
|
||||
)
|
||||
.route_with_tsr("/{workspace_id}/settings/", get(settings_handler::get))
|
||||
.route(
|
||||
"/{workspace_id}/settings/update-name",
|
||||
post(update_name_handler::post),
|
||||
)
|
||||
.route(
|
||||
"/{workspace_id}/add-service-credential",
|
||||
post(add_service_credential_handler::post),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
use askama::Template;
|
||||
use axum::{
|
||||
debug_handler,
|
||||
extract::{Path, State},
|
||||
response::{Html, IntoResponse},
|
||||
};
|
||||
use interim_models::workspace::Workspace;
|
||||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
app::{App, AppDbConn},
|
||||
errors::AppError,
|
||||
navigator::Navigator,
|
||||
settings::Settings,
|
||||
user::CurrentUser,
|
||||
workspace_nav::WorkspaceNav,
|
||||
workspace_pooler::{RoleAssignment, WorkspacePooler},
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct PathParams {
|
||||
workspace_id: Uuid,
|
||||
}
|
||||
|
||||
/// HTTP GET handler for workspace settings, including renaming, access control,
|
||||
/// and deletion.
|
||||
#[debug_handler(state = App)]
|
||||
pub(super) async fn get(
|
||||
State(settings): State<Settings>,
|
||||
CurrentUser(user): CurrentUser,
|
||||
AppDbConn(mut app_db): AppDbConn,
|
||||
Path(PathParams { workspace_id }): Path<PathParams>,
|
||||
navigator: Navigator,
|
||||
State(mut pooler): State<WorkspacePooler>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
// FIXME: Check workspace authorization.
|
||||
// permission to access/alter both as needed.
|
||||
|
||||
let workspace = Workspace::with_id(workspace_id)
|
||||
.fetch_one(&mut app_db)
|
||||
.await?;
|
||||
|
||||
let mut workspace_client = pooler
|
||||
.acquire_for(workspace.id, RoleAssignment::User(user.id))
|
||||
.await?;
|
||||
|
||||
#[derive(Debug, Template)]
|
||||
#[template(path = "workspaces_single/settings.html")]
|
||||
struct ResponseTemplate {
|
||||
settings: Settings,
|
||||
workspace: Workspace,
|
||||
workspace_nav: WorkspaceNav,
|
||||
}
|
||||
Ok(Html(
|
||||
ResponseTemplate {
|
||||
workspace_nav: WorkspaceNav::builder()
|
||||
.navigator(navigator)
|
||||
.workspace(workspace.clone())
|
||||
.populate_rels(&mut app_db, &mut workspace_client)
|
||||
.await?
|
||||
.build()?,
|
||||
workspace,
|
||||
settings,
|
||||
}
|
||||
.render()?,
|
||||
))
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
use axum::{debug_handler, extract::Path, response::Response};
|
||||
use serde::Deserialize;
|
||||
use sqlx::query;
|
||||
use uuid::Uuid;
|
||||
use validator::Validate;
|
||||
|
||||
use crate::{
|
||||
app::{App, AppDbConn},
|
||||
errors::AppError,
|
||||
extractors::ValidatedForm,
|
||||
navigator::{Navigator, NavigatorPage as _},
|
||||
user::CurrentUser,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct PathParams {
|
||||
workspace_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Validate)]
|
||||
pub(super) struct FormBody {
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// HTTP POST handler for updating a workspace's name.
|
||||
#[debug_handler(state = App)]
|
||||
pub(super) async fn post(
|
||||
AppDbConn(mut app_db): AppDbConn,
|
||||
CurrentUser(_user): CurrentUser,
|
||||
navigator: Navigator,
|
||||
Path(PathParams { workspace_id }): Path<PathParams>,
|
||||
ValidatedForm(FormBody { name }): ValidatedForm<FormBody>,
|
||||
) -> Result<Response, AppError> {
|
||||
// FIXME: Check workspace authorization.
|
||||
|
||||
query!(
|
||||
"update workspaces set display_name = $1 where id = $2",
|
||||
name,
|
||||
workspace_id
|
||||
)
|
||||
.execute(app_db.get_conn())
|
||||
.await?;
|
||||
|
||||
Ok(navigator
|
||||
.workspace_page()
|
||||
.workspace_id(workspace_id)
|
||||
.suffix("settings/")
|
||||
.build()?
|
||||
.redirect_to())
|
||||
}
|
||||
|
|
@ -5,10 +5,8 @@
|
|||
<div class="page-grid">
|
||||
<div class="page-grid__toolbar">
|
||||
<div class="page-grid__toolbar-utilities">
|
||||
<a href="settings">
|
||||
<button class="button--secondary" type="button">
|
||||
<a class="button--secondary" href="settings" role="button">
|
||||
Portal Settings
|
||||
</button>
|
||||
</a>
|
||||
<filter-menu
|
||||
identifier-hints="{{ attr_names | json }}"
|
||||
|
|
|
|||
|
|
@ -3,17 +3,22 @@
|
|||
{% block main %}
|
||||
<div class="page-grid">
|
||||
<div class="page-grid__toolbar">
|
||||
<a href="{{ navigator.portal_page()
|
||||
<div class="page-grid__toolbar-utilities">
|
||||
<a
|
||||
class="button--secondary"
|
||||
href="{{ navigator.portal_page()
|
||||
.workspace_id(*portal.workspace_id)
|
||||
.rel_oid(*portal.class_oid)
|
||||
.portal_id(*portal.id)
|
||||
.build()?
|
||||
.get_path() }}">
|
||||
<button class="button--secondary" style="margin-left: 0.5rem;" type="button">
|
||||
.get_path() }}"
|
||||
role="button"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
{% include "toolbar_user.html" %}
|
||||
</div>
|
||||
<div class="page-grid__sidebar">
|
||||
<div style="padding: 1rem;">
|
||||
{{ workspace_nav | safe }}
|
||||
|
|
@ -22,7 +27,7 @@
|
|||
<main class="page-grid__main padded--lg">
|
||||
<form method="post" action="update-name">
|
||||
<section>
|
||||
<h1>Name</h1>
|
||||
<h1>Portal Name</h1>
|
||||
<input type="text" name="name" value="{{ portal.name }}">
|
||||
<button class="button--primary" type="submit">Save</button>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@
|
|||
{% block main %}
|
||||
<div class="page-grid">
|
||||
<div class="page-grid__toolbar">
|
||||
{% include "toolbar_user.html" %}
|
||||
</div>
|
||||
<div class="page-grid__sidebar">
|
||||
<div style="padding: 1rem;">
|
||||
{{ workspace_nav | safe }}
|
||||
</div>
|
||||
</div>
|
||||
<main class="page-grid__main">
|
||||
<main class="page-grid__main padded--lg">
|
||||
<form method="post" action="update-name">
|
||||
<section>
|
||||
<h1>Name</h1>
|
||||
<h1>Table Name</h1>
|
||||
<input type="text" name="name" value="{{ rel.relname }}">
|
||||
<button class="button--primary" type="submit">Save</button>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -8,14 +8,22 @@
|
|||
{% endif %}
|
||||
</h1>
|
||||
<basic-dropdown button-class="button--secondary button--small" button-aria-label="Workspace Menu">
|
||||
<span slot="button-contents"><i class="ti ti-dots" aria-hidden="true"></i></span>
|
||||
<span slot="button-contents"><i class="ti ti-dots-vertical" aria-hidden="true"></i></span>
|
||||
<menu slot="popover" class="basic-dropdown__menu">
|
||||
<li>
|
||||
<a
|
||||
href="{{ navigator.get_root_path() }}/w/{{ workspace.id.simple() }}/service-credentials"
|
||||
role="button"
|
||||
>
|
||||
PostgreSQL Credentials
|
||||
PostgreSQL credentials
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="{{ navigator.get_root_path() }}/w/{{ workspace.id.simple() }}/settings"
|
||||
role="button"
|
||||
>
|
||||
Workspace settings
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -23,7 +31,7 @@
|
|||
href="{{ navigator.get_root_path() }}/"
|
||||
role="button"
|
||||
>
|
||||
All Workspaces
|
||||
All workspaces
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
|
|
@ -39,7 +47,9 @@
|
|||
method="post"
|
||||
>
|
||||
<!-- FIXME: CSRF -->
|
||||
<button class="workspace-nav__aux-button" type="submit">+</button>
|
||||
<button aria-label="Add table" class="button--secondary button--small" type="submit">
|
||||
<i class="ti ti-database-plus"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<menu class="workspace-nav__menu">
|
||||
|
|
@ -57,16 +67,22 @@
|
|||
>
|
||||
<div class="workspace-nav__heading" slot="summary">
|
||||
<h3>{{ rel.name }}</h3>
|
||||
<basic-dropdown button-class="button--secondary button--small" button-aria-label="Table menu">
|
||||
<span slot="button-contents"><i class="ti ti-dots-vertical" aria-hidden="true"></i></span>
|
||||
<menu slot="popover" class="basic-dropdown__menu">
|
||||
<li>
|
||||
<a
|
||||
href="{{ navigator.get_root_path() -}}
|
||||
/w/{{ workspace.id.simple() -}}
|
||||
/r/{{ rel.oid.0 -}}
|
||||
/settings/"
|
||||
class="workspace-nav__aux-button"
|
||||
role="button"
|
||||
>
|
||||
Settings
|
||||
Table settings
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</basic-dropdown>
|
||||
</div>
|
||||
<menu class="workspace-nav__menu" slot="content">
|
||||
<li class="workspace-nav__menu-item">
|
||||
|
|
@ -81,7 +97,9 @@
|
|||
method="post"
|
||||
>
|
||||
<!-- FIXME: CSRF -->
|
||||
<button class="workspace-nav__aux-button" type="submit">+</button>
|
||||
<button aria-label="Add portal" class="workspace-nav__aux-button" type="submit">
|
||||
<i class="ti ti-table-plus"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<menu slot="content" class="workspace-nav__menu">
|
||||
|
|
@ -103,17 +121,35 @@
|
|||
>
|
||||
{{ portal.name }}
|
||||
</a>
|
||||
<basic-dropdown button-class="button--secondary button--small" button-aria-label="Portal menu">
|
||||
<span slot="button-contents"><i class="ti ti-dots-vertical" aria-hidden="true"></i></span>
|
||||
<menu slot="popover" class="basic-dropdown__menu">
|
||||
<li>
|
||||
<a
|
||||
href="{{ navigator.get_root_path() -}}
|
||||
/w/{{ workspace.id.simple() -}}
|
||||
/r/{{ rel.oid.0 -}}
|
||||
/p/{{ portal.id.simple() -}}
|
||||
/settings/"
|
||||
role="button"
|
||||
>
|
||||
Portal settings
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="{{ navigator.get_root_path() -}}
|
||||
/w/{{ workspace.id.simple() -}}
|
||||
/r/{{ rel.oid.0 -}}
|
||||
/p/{{ portal.id.simple() -}}
|
||||
/form/"
|
||||
class="workspace-nav__aux-button"
|
||||
role="button"
|
||||
>
|
||||
Form
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</basic-dropdown>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
29
interim-server/templates/workspaces_single/settings.html
Normal file
29
interim-server/templates/workspaces_single/settings.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block main %}
|
||||
<div class="page-grid">
|
||||
<div class="page-grid__toolbar">
|
||||
{% include "toolbar_user.html" %}
|
||||
</div>
|
||||
<div class="page-grid__sidebar">
|
||||
<div style="padding: 1rem;">
|
||||
{{ workspace_nav | safe }}
|
||||
</div>
|
||||
</div>
|
||||
<main class="page-grid__main padded--lg">
|
||||
<form method="post" action="update-name">
|
||||
<section>
|
||||
<h1>Workspace Name</h1>
|
||||
<input type="text" name="name" value="{{ workspace.display_name }}">
|
||||
<button class="button--primary" type="submit">Save</button>
|
||||
</section>
|
||||
</form>
|
||||
<form method="post" action="">
|
||||
<section>
|
||||
<h1>Sharing</h1>
|
||||
<button class="button--primary" type="submit">Save</button>
|
||||
</section>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -58,18 +58,22 @@ button, input[type="submit"] {
|
|||
|
||||
.button {
|
||||
&--primary {
|
||||
@include globals.reset-anchor;
|
||||
@include globals.button-primary;
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
@include globals.reset-anchor;
|
||||
@include globals.button-secondary;
|
||||
}
|
||||
|
||||
&--clear {
|
||||
@include globals.reset-anchor;
|
||||
@include globals.button-clear;
|
||||
}
|
||||
|
||||
&--small {
|
||||
@include globals.reset-anchor;
|
||||
@include globals.button-small;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue