1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-01-18 13:07:58 +00:00

Many small tweaks

This commit is contained in:
Romein van Buren 2023-01-11 20:41:15 +01:00
parent f4bf270a20
commit 90ffdc6a8b
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
11 changed files with 223 additions and 171 deletions

View File

@ -11,7 +11,7 @@
let activeHostKey = '';
let activeDbKey = '';
let activeCollKey = '';
let addressBarModalOpen = false;
let addressBarModalOpen = true;
$: host = hosts[activeHostKey];
$: connection = connections[activeHostKey];
@ -61,60 +61,46 @@
<main>
<AddressBar {hosts} bind:activeHostKey on:select={e => openConnection(e.detail)} bind:modalOpen={addressBarModalOpen} />
<div class="columns">
{#if host && connection}
<div class="hostlist">
<Grid
columns={[ { key: 'id' }, { key: 'collCount', right: true } ]}
items={Object.keys(connection.databases).map(id => ({
id,
collCount: Object.keys(connection.databases[id].collections || {}).length,
children: connection.databases[id].collections || [],
}))}
bind:activeKey={activeDbKey}
bind:activeChildKey={activeCollKey}
on:select={e => openDatabase(e.detail)}
on:selectChild={e => openCollection(e.detail)}
/>
</div>
{#if host && connection}
<div class="hostlist">
<Grid
columns={[ { key: 'id' }, { key: 'collCount', right: true } ]}
items={Object.keys(connection.databases).map(id => ({
id,
collCount: Object.keys(connection.databases[id].collections || {}).length,
children: connection.databases[id].collections || [],
}))}
bind:activeKey={activeDbKey}
bind:activeChildKey={activeCollKey}
on:select={e => openDatabase(e.detail)}
on:selectChild={e => openCollection(e.detail)}
/>
</div>
<div class="collection">
<CollectionDetail
{collection}
hostKey={activeHostKey}
dbKey={activeDbKey}
collectionKey={activeCollKey}
/>
</div>
{/if}
</div>
<div class="collection">
<CollectionDetail
{collection}
hostKey={activeHostKey}
dbKey={activeDbKey}
collectionKey={activeCollKey}
/>
</div>
{/if}
</main>
<style>
main {
height: 100vh;
display: flex;
flex-flow: column;
display: grid;
grid-template: 3rem auto / 250px 1fr;
gap: 0.5rem;
padding: 0.5rem;
}
.columns {
display: flex;
gap: 1rem;
flex: 1;
height: 100%;
}
.columns > :global(*) {
height: 100%;
display: flex;
flex-flow: column;
main > :global(.addressbar) {
grid-column: 1 / 3;
}
.hostlist {
flex: 0 0 250px;
overflow: scroll;
}
.collection {
flex: 1;
width: auto;
}
</style>

View File

@ -13,7 +13,6 @@
border-radius: 10px;
padding: 0.5rem;
opacity: 0.6;
margin-bottom: 0.5rem;
}
strong {

View File

@ -107,8 +107,7 @@
.grid {
background-color: #fff;
width: 100%;
overflow: scroll;
max-height: 400px; /* fixme */
height: 100%;
}
.grid.contained {
border: 1px solid #ccc;

View File

@ -18,8 +18,13 @@
<div class="addressbar">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="address" class:empty={!host?.uri} on:click={() => modalOpen = true}>
{host?.uri || 'No host selected'}
<div class="address" on:click={() => modalOpen = true}>
{#if host?.uri}
{@const split = host.uri.split('://').map(s => s.split('/')).flat()}
<span class="protocol">{split[0]}://</span><span class="hostname">{split[1]}</span><span class="path">{split.slice(2).join('/')}</span>
{:else}
<span class="empty">no host selected</span>
{/if}
</div>
<div class="actions">
@ -48,14 +53,15 @@
display: flex;
justify-content: space-between;
align-items: center;
margin: 1rem;
padding: 0.5rem 0.5rem 0.5rem 1rem;
height: 3rem;
border: 1px solid #ccc;
border-radius: 10px;
}
.address.empty {
font-style: italic;
.address .protocol,
.address .path,
.address .empty {
opacity: 0.6;
}

View File

@ -24,20 +24,19 @@
limit: 30,
};
let result = [];
let result = {};
let submittedForm = {};
let queryField;
let activeKey = '';
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
$: if (collection) {
result = [];
}
async function submitQuery() {
activeKey = '';
result = await PerformFind(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form));
queryField?.focus();
queryField?.select();
if (result) {
submittedForm = JSON.parse(JSON.stringify(form));
}
resetFocus();
}
function prev() {
@ -58,92 +57,104 @@
alert('yet to be implemented');
}
onMount(() => {
function resetFocus() {
queryField?.focus();
queryField?.select();
});
}
onMount(resetFocus);
</script>
<form on:submit|preventDefault={submitQuery}>
<div class="row-one">
<label class="field">
<span class="label">Query or id</span>
<input type="text" class="code" bind:this={queryField} bind:value={form.query} use:input={{ json: true }} placeholder={defaults.query} />
</label>
<div class="find">
<form on:submit|preventDefault={submitQuery}>
<div class="form-row one">
<label class="field">
<span class="label">Query or id</span>
<input type="text" class="code" bind:this={queryField} bind:value={form.query} use:input={{ json: true }} placeholder={defaults.query} />
</label>
<label class="field">
<span class="label">Sort</span>
<input type="text" class="code" bind:value={form.sort} use:input={{ json: true }} placeholder={defaults.sort} />
</label>
</div>
<div class="row-two">
<label class="field">
<span class="label">Fields</span>
<input type="text" class="code" bind:value={form.fields} use:input={{ json: true }} placeholder={defaults.fields} />
</label>
<label class="field">
<span class="label">Skip</span>
<input type="number" min="0" bind:value={form.skip} use:input placeholder={defaults.skip} />
</label>
<label class="field">
<span class="label">Limit</span>
<input type="number" min="0" bind:value={form.limit} use:input placeholder={defaults.limit} />
</label>
<button type="submit" class="btn">Run</button>
</div>
</form>
<CodeExample {code} />
<div class="result">
<ObjectGrid data={result} bind:activeKey />
<div class="controls">
<div>
{#if result}
Results: {result.length}
{/if}
<label class="field">
<span class="label">Sort</span>
<input type="text" class="code" bind:value={form.sort} use:input={{ json: true }} placeholder={defaults.sort} />
</label>
</div>
<div>
<button class="btn danger" on:click={remove} disabled={!activeKey}>
<Icon name="-" />
</button>
<button class="btn" on:click={prev} disabled={!form.limit || (form.skip <= 0) || !result?.length}>
<Icon name="chev-l" />
</button>
<button class="btn" on:click={next} disabled={!form.limit || ((result?.length || Infinity) < form.limit) || !result?.length}>
<Icon name="chev-r" />
</button>
<div class="form-row two">
<label class="field">
<span class="label">Fields</span>
<input type="text" class="code" bind:value={form.fields} use:input={{ json: true }} placeholder={defaults.fields} />
</label>
<label class="field">
<span class="label">Skip</span>
<input type="number" min="0" bind:value={form.skip} use:input placeholder={defaults.skip} />
</label>
<label class="field">
<span class="label">Limit</span>
<input type="number" min="0" bind:value={form.limit} use:input placeholder={defaults.limit} />
</label>
<button type="submit" class="btn">Run</button>
</div>
</form>
<CodeExample {code} />
<div class="result">
<div class="grid">
{#key result}
<ObjectGrid data={result.results} bind:activeKey />
{/key}
</div>
<div class="controls">
<div>
{#if result}
Results: {result.total || 0}
{/if}
</div>
<div>
<button class="btn danger" on:click={remove} disabled={!activeKey}>
<Icon name="-" />
</button>
<button class="btn" on:click={prev} disabled={!submittedForm.limit || (submittedForm.skip <= 0) || !result?.results?.length}>
<Icon name="chev-l" />
</button>
<button class="btn" on:click={next} disabled={!submittedForm.limit || ((result?.results?.length || 0) < submittedForm.limit) || !result?.results?.length}>
<Icon name="chev-r" />
</button>
</div>
</div>
</div>
</div>
<style>
.row-one {
.find {
display: grid;
gap: 0.5rem;
grid-template-columns: 3fr 2fr;
grid-template: auto auto 1fr / 1fr;
}
.form-row {
display: grid;
gap: 0.5rem;
}
.form-row.one {
grid-template: 1fr / 3fr 2fr;
margin-bottom: 0.5rem;
}
.row-two {
display: grid;
gap: 0.5rem;
grid-template-columns: 5fr 1fr 1fr 1fr;
margin-bottom: 0.5rem;
.form-row.two {
grid-template: 1fr / 5fr 1fr 1fr 1fr;
}
.result {
flex: 1;
display: flex;
flex-flow: column;
margin-top: 0.5rem;
display: grid;
grid-template: 1fr auto / 1fr;
gap: 0.5rem;
}
.result > :global(.grid) {
flex: 1;
.result > .grid {
overflow: auto;
}
.result > .controls {
display: flex;

View File

@ -1,11 +1,10 @@
<script>
import ObjectGrid from '../../components/objectgrid.svelte';
import CodeExample from '../../components/code-example.svelte';
import TabBar from '../../components/tabbar.svelte';
import Find from './find.svelte';
import Indexes from './indexes.svelte';
import Insert from './insert.svelte';
import Remove from './remove.svelte';
import Stats from './stats.svelte';
export let collection;
export let hostKey;
@ -23,29 +22,25 @@
<div class="collection" class:empty={!collection}>
{#if collection}
<TabBar tabs={[
{ key: 'stats', title: 'Stats' },
{ key: 'find', title: 'Find' },
{ key: 'insert', title: 'Insert' },
{ key: 'update', title: 'Update' },
{ key: 'remove', title: 'Remove' },
{ key: 'indexes', title: 'Indexes' },
]} bind:selectedKey={tab} />
{#key collection}
<TabBar tabs={[
{ key: 'stats', title: 'Stats' },
{ key: 'find', title: 'Find' },
{ key: 'insert', title: 'Insert' },
{ key: 'update', title: 'Update' },
{ key: 'remove', title: 'Remove' },
{ key: 'indexes', title: 'Indexes' },
]} bind:selectedKey={tab} />
<div class="container">
{#if tab === 'stats'}
<CodeExample code="db.stats()" />
<ObjectGrid data={collection.stats} />
{:else if tab === 'find'}
<Find {collection} />
{:else if tab === 'insert'}
<Insert {collection} />
{:else if tab === 'remove'}
<Remove {collection} />
{:else if tab === 'indexes'}
<Indexes {collection} />
{/if}
</div>
<div class="container">
{#if tab === 'stats'} <Stats {collection} />
{:else if tab === 'find'} <Find {collection} />
{:else if tab === 'insert'} <Insert {collection} />
{:else if tab === 'remove'} <Remove {collection} />
{:else if tab === 'indexes'} <Indexes {collection} />
{/if}
</div>
{/key}
{:else}
No collection selected
{/if}
@ -53,16 +48,18 @@
<style>
.collection {
margin: 1rem 1rem 1rem 0;
height: 100%;
display: flex;
flex-flow: column;
display: grid;
grid-template: auto 1fr / 1fr;
gap: 0.5rem;
}
.container {
padding: 0.5rem 0.5rem 0;
flex: 1;
padding: 0 0.5rem;
display: flex;
flex-flow: column;
align-items: stretch;
}
.container > :global(*) {
width: 100%;
}
</style>

View File

@ -6,10 +6,6 @@
let input = '';
let insertedIds;
$: if (collection) {
insertedIds = undefined;
}
async function insert() {
insertedIds = await PerformInsert(collection.hostKey, collection.dbKey, collection.key, input);
}

View File

@ -0,0 +1,19 @@
<script>
import ObjectGrid from '../../components/objectgrid.svelte';
import CodeExample from '../../components/code-example.svelte';
export let collection;
</script>
<div class="stats">
<CodeExample code="db.stats()" />
<ObjectGrid data={collection.stats} />
</div>
<style>
.stats {
display: grid;
gap: 0.5rem;
grid-template: auto 1fr / 1fr;
}
</style>

View File

@ -2,6 +2,7 @@
// This file is automatically generated. DO NOT EDIT
import {map[string]main} from '../models';
import {primitive} from '../models';
import {main} from '../models';
export function Hosts():Promise<map[string]main.Host>;
@ -11,6 +12,6 @@ export function OpenConnection(arg1:string):Promise<Array<string>>;
export function OpenDatabase(arg1:string,arg2:string):Promise<Array<string>>;
export function PerformFind(arg1:string,arg2:string,arg3:string,arg4:string):Promise<any>;
export function PerformFind(arg1:string,arg2:string,arg3:string,arg4:string):Promise<main.findResult>;
export function PerformInsert(arg1:string,arg2:string,arg3:string,arg4:string):Promise<any>;

19
frontend/wailsjs/go/models.ts Executable file
View File

@ -0,0 +1,19 @@
export namespace main {
export class findResult {
total: number;
results: any;
static createFrom(source: any = {}) {
return new findResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.total = source["total"];
this.results = source["results"];
}
}
}

39
main.go
View File

@ -141,7 +141,13 @@ func (a *App) OpenCollection(hostKey, dbKey, collKey string) (result bson.M) {
return result
}
func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) interface{} {
type findResult struct {
Total int64 `json:"total"`
Results interface{} `json:"results"`
}
func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) findResult {
var out findResult
var form struct {
Fields string `json:"fields"`
Limit int64 `json:"limit"`
@ -158,13 +164,13 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Title: "Couldn't parse form",
Message: err.Error(),
})
return nil
return out
}
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return nil
return out
}
defer close()
@ -180,7 +186,7 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Title: "Invalid query",
Message: err.Error(),
})
return nil
return out
}
err = json.Unmarshal([]byte(form.Fields), &projection)
@ -191,7 +197,7 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Title: "Invalid projection",
Message: err.Error(),
})
return nil
return out
}
err = json.Unmarshal([]byte(form.Sort), &sort)
@ -202,7 +208,7 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Title: "Invalid sort",
Message: err.Error(),
})
return nil
return out
}
opt := mongoOptions.FindOptions{
@ -212,7 +218,18 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Sort: sort,
}
cur, err := client.Database(dbKey).Collection(collKey).Find(ctx, bson.D{}, &opt)
total, err := client.Database(dbKey).Collection(collKey).CountDocuments(ctx, query, nil)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Encountered an error while counting documents",
Message: err.Error(),
})
return out
}
cur, err := client.Database(dbKey).Collection(collKey).Find(ctx, query, &opt)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
@ -220,7 +237,7 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Title: "Encountered an error while performing query",
Message: err.Error(),
})
return nil
return out
}
defer cur.Close(ctx)
@ -234,10 +251,12 @@ func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) inter
Title: "Encountered an error while performing query",
Message: err.Error(),
})
return nil
return out
}
return results
out.Results = results
out.Total = total
return out
}
func (a *App) PerformInsert(hostKey, dbKey, collKey, jsonData string) interface{} {