mirror of
https://github.com/garraflavatra/rolens.git
synced 2025-01-18 13:07:58 +00:00
Layout changes. Edit collection names.
This commit is contained in:
parent
e83a02fae8
commit
4d50d43f4d
1
frontend/package.json.md5
Executable file
1
frontend/package.json.md5
Executable file
@ -0,0 +1 @@
|
||||
e25516ba8abdacf5ab40609a5d48f542
|
@ -1,19 +1,15 @@
|
||||
<script>
|
||||
import { OpenConnection } from '../wailsjs/go/app/App';
|
||||
import { EventsOn, WindowSetTitle } from '../wailsjs/runtime';
|
||||
import BlankState from './components/blankstate.svelte';
|
||||
import { EventsOn } from '../wailsjs/runtime';
|
||||
import ContextMenu from './components/contextmenu.svelte';
|
||||
import About from './organisms/about/index.svelte';
|
||||
import AddressBar from './organisms/addressbar/index.svelte';
|
||||
import Connection from './organisms/connection/index.svelte';
|
||||
import Settings from './organisms/settings/index.svelte';
|
||||
import { applicationSettings, busy, connections, contextMenu, environment } from './stores';
|
||||
import { applicationSettings, connections, contextMenu, environment } from './stores';
|
||||
|
||||
let hosts = {};
|
||||
let activeHostKey = '';
|
||||
const hosts = {};
|
||||
const activeHostKey = '';
|
||||
let activeDbKey = '';
|
||||
let activeCollKey = '';
|
||||
let addressBarModalOpen = true;
|
||||
let settingsModalOpen = false;
|
||||
let aboutModalOpen = false;
|
||||
|
||||
@ -23,26 +19,7 @@
|
||||
$: collection = database?.collections?.[activeCollKey];
|
||||
|
||||
EventsOn('OpenPrefrences', () => settingsModalOpen = true);
|
||||
EventsOn('OpenHostsModal', () => addressBarModalOpen = true);
|
||||
EventsOn('OpenAboutModal', () => aboutModalOpen = true);
|
||||
|
||||
async function openConnection(hostKey) {
|
||||
busy.start();
|
||||
const databases = await OpenConnection(hostKey);
|
||||
|
||||
if (databases) {
|
||||
$connections[hostKey] = { databases: {} };
|
||||
databases.forEach(dbKey => {
|
||||
$connections[hostKey].databases[dbKey] = { collections: {} };
|
||||
});
|
||||
activeHostKey = hostKey;
|
||||
addressBarModalOpen = false;
|
||||
WindowSetTitle(`${hosts[activeHostKey].name} - Rolens`);
|
||||
}
|
||||
|
||||
busy.end();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:window on:contextmenu|preventDefault />
|
||||
@ -52,13 +29,7 @@
|
||||
|
||||
{#if $environment && $applicationSettings}
|
||||
<main class:empty={!host || !connection}>
|
||||
<AddressBar bind:hosts bind:activeHostKey on:select={e => openConnection(e.detail)} bind:modalOpen={addressBarModalOpen} />
|
||||
|
||||
{#if host && connection}
|
||||
<Connection {hosts} bind:activeCollKey bind:activeDbKey {activeHostKey} />
|
||||
{:else}
|
||||
<BlankState label="A database client is nothing without a host" image="/fish.svg" />
|
||||
{/if}
|
||||
<Connection {hosts} bind:activeCollKey bind:activeDbKey {activeHostKey} />
|
||||
</main>
|
||||
|
||||
{#key $contextMenu}
|
||||
@ -82,16 +53,11 @@
|
||||
main {
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template: 3rem auto / 250px 1fr;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
grid-template: 1fr / 250px 1fr;
|
||||
}
|
||||
#root.platform-darwin main {
|
||||
height: calc(100vh - var(--darwin-titlebar-height));
|
||||
}
|
||||
main.empty {
|
||||
grid-template: 3rem auto / 1fr;
|
||||
}
|
||||
|
||||
main > :global(*) {
|
||||
overflow: auto;
|
||||
|
@ -5,11 +5,16 @@
|
||||
export let position = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let selected = -1;
|
||||
const buttons = [];
|
||||
let selected = -1;
|
||||
|
||||
$: if (items && position) {
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
function close() {
|
||||
dispatch('close');
|
||||
selected = -1;
|
||||
}
|
||||
|
||||
function click(fn) {
|
||||
|
4
frontend/src/components/icon.svelte
vendored
4
frontend/src/components/icon.svelte
vendored
@ -31,6 +31,8 @@
|
||||
<path d="m11 17-5-5 5-5M18 17l-5-5 5-5"/>
|
||||
{:else if name === 'chevs-r'}
|
||||
<path d="m13 17 5-5-5-5M6 17l5-5-5-5"/>
|
||||
{:else if name === 'arr-d'}
|
||||
<path d="M12 5v14M19 12l-7 7-7-7"/>
|
||||
{:else if name === 'db'}
|
||||
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
|
||||
{:else if name === 'x'}
|
||||
@ -55,5 +57,7 @@
|
||||
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
{:else if name === 'zap'}
|
||||
<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>
|
||||
{:else if name === 'server'}
|
||||
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><path d="M6 6h.01M6 18h.01"/>
|
||||
{/if}
|
||||
</svg>
|
||||
|
@ -1,148 +0,0 @@
|
||||
<script>
|
||||
import Modal from '../../components/modal.svelte';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import Icon from '../../components/icon.svelte';
|
||||
import { Hosts, RemoveHost } from '../../../wailsjs/go/app/App';
|
||||
import Welcome from './welcome.svelte';
|
||||
import HostDetail from './hostdetail.svelte';
|
||||
|
||||
export let hosts = {};
|
||||
export let activeHostKey = '';
|
||||
export let modalOpen = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let error = '';
|
||||
let hostDetailModalOpen = false;
|
||||
let hostDetailModalHost;
|
||||
let hostDetailModalKey = '';
|
||||
$: host = hosts?.[activeHostKey];
|
||||
$: hostCount = Object.keys(hosts).length;
|
||||
|
||||
$: if (!modalOpen) {
|
||||
error = '';
|
||||
}
|
||||
|
||||
function select(hostKey) {
|
||||
activeHostKey = hostKey;
|
||||
dispatch('select', hostKey);
|
||||
}
|
||||
|
||||
async function getHosts() {
|
||||
try {
|
||||
const h = await Hosts();
|
||||
hosts = h || {};
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
|
||||
function createHost() {
|
||||
hostDetailModalHost = undefined;
|
||||
hostDetailModalKey = '';
|
||||
hostDetailModalOpen = true;
|
||||
}
|
||||
|
||||
function editHost(hostKey) {
|
||||
hostDetailModalHost = hosts[hostKey];
|
||||
hostDetailModalKey = hostKey;
|
||||
hostDetailModalOpen = true;
|
||||
}
|
||||
|
||||
async function removeHost(hostKey) {
|
||||
try {
|
||||
await RemoveHost(hostKey);
|
||||
await getHosts();
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(getHosts);
|
||||
</script>
|
||||
|
||||
<Modal bind:show={modalOpen} title={hostCount && 'Hosts'} width="60vw" overflow={false}>
|
||||
{#if hostCount}
|
||||
<div class="status">
|
||||
<p class:error>
|
||||
{#if error}
|
||||
<strong>Oops!</strong> {error}
|
||||
{:else}
|
||||
{hostCount} host{hostCount === 1 ? '' : 's'}
|
||||
{/if}
|
||||
</p>
|
||||
<button class="btn" on:click={createHost}>
|
||||
Create new host
|
||||
</button>
|
||||
</div>
|
||||
<ul class="hosts">
|
||||
{#each Object.entries(hosts) as [hostKey, host]}
|
||||
<li>
|
||||
<div class="host">
|
||||
<button class="btn secondary" title="Connect to {host.name}" on:click={() => select(hostKey)}>
|
||||
{host.name}
|
||||
</button>
|
||||
<button class="btn secondary" title="Edit {host.name}" on:click={() => editHost(hostKey)}>
|
||||
<Icon name="edit" />
|
||||
</button>
|
||||
<button class="btn secondary" title="Remove {host.name}" on:click={() => removeHost(hostKey)}>
|
||||
<Icon name="x" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<Welcome on:createHost={createHost} />
|
||||
{/if}
|
||||
</Modal>
|
||||
|
||||
<HostDetail
|
||||
host={hostDetailModalHost}
|
||||
hostKey={hostDetailModalKey}
|
||||
on:reload={getHosts}
|
||||
bind:show={hostDetailModalOpen}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.hosts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.error {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.host {
|
||||
display: grid;
|
||||
grid-template: 1fr 1fr / 1fr auto;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.host button {
|
||||
border-radius: 0;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
.host button:nth-child(1) {
|
||||
border-right: none;
|
||||
grid-row: 1 / 3;
|
||||
height: 100%;
|
||||
border-radius: 10px 0 0 10px;
|
||||
}
|
||||
.host button:nth-child(2) {
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.host button:nth-child(3) {
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
</style>
|
@ -1,49 +0,0 @@
|
||||
<script>
|
||||
import Icon from '../../components/icon.svelte';
|
||||
import HostModal from './hostmodal.svelte';
|
||||
|
||||
export let hosts = {};
|
||||
export let activeHostKey = '';
|
||||
export let modalOpen = false;
|
||||
|
||||
$: host = hosts?.[activeHostKey];
|
||||
</script>
|
||||
|
||||
<div class="addressbar">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<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">
|
||||
<button class="btn" on:click={() => modalOpen = true}>
|
||||
<Icon name="db" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<HostModal bind:modalOpen bind:hosts on:select />
|
||||
|
||||
<style>
|
||||
.addressbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem 0.5rem 1rem;
|
||||
height: 3rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.address .protocol,
|
||||
.address .path,
|
||||
.address .empty {
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
@ -5,13 +5,14 @@
|
||||
import Modal from '../../components/modal.svelte';
|
||||
|
||||
export let show = false;
|
||||
export let host = undefined;
|
||||
export let hostKey = '';
|
||||
export let hosts = {};
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let form = { ...(host || {}) };
|
||||
let form = {};
|
||||
let error = '';
|
||||
$: valid = validate(form);
|
||||
$: host = hosts[hostKey];
|
||||
|
||||
$: if (show || !show) {
|
||||
init();
|
@ -3,7 +3,7 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { DropCollection, DropDatabase, OpenCollection, OpenConnection, OpenDatabase } from '../../../wailsjs/go/app/App';
|
||||
import Grid from '../../components/grid.svelte';
|
||||
import { WindowSetTitle } from '../../../wailsjs/runtime';
|
||||
import { WindowSetTitle } from '../../../wailsjs/runtime/runtime';
|
||||
|
||||
export let hosts = {};
|
||||
export let activeHostKey = '';
|
||||
@ -11,9 +11,10 @@
|
||||
export let activeCollKey = '';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let dbAndCollKeys = [];
|
||||
$: activeDbKey = dbAndCollKeys[0];
|
||||
$: activeCollKey = dbAndCollKeys[1];
|
||||
let activeGridPath = [];
|
||||
$: activeHostKey = activeGridPath[0] || activeHostKey;
|
||||
$: activeDbKey = activeGridPath[1];
|
||||
$: activeCollKey = activeGridPath[2];
|
||||
$: host = hosts[activeHostKey];
|
||||
$: connection = $connections[activeHostKey];
|
||||
$: database = connection?.databases[activeDbKey];
|
||||
@ -74,67 +75,64 @@
|
||||
await reload();
|
||||
busy.end();
|
||||
}
|
||||
|
||||
function buildMenu(hostKey, dbKey, collKey, context = [ 'new', 'edit', 'drop' ]) {
|
||||
// context: n = new, d = drop
|
||||
const menu = [];
|
||||
|
||||
if (context.includes('edit')) {
|
||||
hostKey && menu.push({ label: `Edit host ${hosts[hostKey].name}…`, fn: () => dispatch('editHost', hostKey) });
|
||||
collKey && menu.push({ label: `Rename collection ${collKey}…`, fn: () => dispatch('editCollection', collKey) });
|
||||
}
|
||||
if (context.includes('drop')) {
|
||||
menu.length && menu.push({ separator: true });
|
||||
dbKey && menu.push({ label: `Drop database ${dbKey}…`, fn: () => dropDatabase(dbKey) });
|
||||
collKey && menu.push({ label: `Drop collection ${collKey}…`, fn: () => dropCollection(dbKey, collKey) });
|
||||
}
|
||||
if (context.includes('new')) {
|
||||
menu.length && menu.push({ separator: true });
|
||||
hostKey && menu.push({ label: 'New database…', fn: () => dispatch('newDatabase') });
|
||||
dbKey && menu.push({ label: 'New collection…', fn: () => dispatch('newCollection') });
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if host && connection}
|
||||
<Grid
|
||||
striped={false}
|
||||
columns={[ { key: 'id' }, { key: 'collCount', right: true } ]}
|
||||
items={Object.keys(connection.databases).sort().map(dbKey => ({
|
||||
<Grid
|
||||
striped={false}
|
||||
columns={[ { key: 'name' }, { key: 'count', right: true } ]}
|
||||
items={Object.keys(hosts).map(hostKey => ({
|
||||
id: hostKey,
|
||||
name: hosts[hostKey].name,
|
||||
icon: 'server',
|
||||
children: Object.keys(connection?.databases || {}).sort().map(dbKey => ({
|
||||
id: dbKey,
|
||||
name: dbKey,
|
||||
icon: 'db',
|
||||
collCount: Object.keys(connection.databases[dbKey].collections || {}).length || '',
|
||||
count: Object.keys(connection.databases[dbKey].collections || {}).length || '',
|
||||
children: Object.keys(connection.databases[dbKey].collections).sort().map(collKey => ({
|
||||
id: collKey,
|
||||
name: collKey,
|
||||
icon: 'list',
|
||||
menu: [
|
||||
{ label: `Drop ${collKey}…`, fn: () => dropCollection(dbKey, collKey) },
|
||||
{ label: `Drop ${dbKey}…`, fn: () => dropDatabase(dbKey) },
|
||||
{ separator: true },
|
||||
{ label: 'New database…', fn: () => dispatch('newDatabase') },
|
||||
{ label: 'New collection…', fn: () => dispatch('newCollection') },
|
||||
],
|
||||
menu: buildMenu(hostKey, dbKey, collKey),
|
||||
})) || [],
|
||||
menu: [
|
||||
{ label: `Drop ${dbKey}…`, fn: () => dropDatabase(dbKey) },
|
||||
{ separator: true },
|
||||
{ label: 'New database…', fn: () => dispatch('newDatabase') },
|
||||
{ label: 'New collection…', fn: () => dispatch('newCollection') },
|
||||
],
|
||||
}))}
|
||||
actions={[
|
||||
{ icon: 'reload', fn: reload },
|
||||
{ icon: '+', fn: evt => {
|
||||
if (activeDbKey) {
|
||||
contextMenu.show(evt, [
|
||||
{ label: 'New database…', fn: () => dispatch('newDatabase') },
|
||||
{ label: 'New collection…', fn: () => dispatch('newCollection') },
|
||||
]);
|
||||
}
|
||||
else {
|
||||
dispatch('newDatabase');
|
||||
}
|
||||
} },
|
||||
{ icon: '-', fn: evt => {
|
||||
if (activeCollKey) {
|
||||
contextMenu.show(evt, [
|
||||
{ label: 'Drop database…', fn: () => dropDatabase(activeDbKey) },
|
||||
{ label: 'Drop collection…', fn: () => dropCollection(activeDbKey, activeCollKey) },
|
||||
]);
|
||||
}
|
||||
else {
|
||||
dropDatabase(activeDbKey);
|
||||
}
|
||||
}, disabled: !activeDbKey },
|
||||
]}
|
||||
bind:activePath={dbAndCollKeys}
|
||||
on:select={e => {
|
||||
if (e.detail?.level === 0) {
|
||||
openDatabase(e.detail.itemKey);
|
||||
}
|
||||
else if (e.detail?.level === 1) {
|
||||
openCollection(e.detail.itemKey);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
menu: buildMenu(hostKey, dbKey),
|
||||
})),
|
||||
}))}
|
||||
actions={[
|
||||
{ icon: 'reload', fn: reload },
|
||||
{ icon: '+', fn: evt => contextMenu.show(evt, buildMenu(activeHostKey, activeDbKey, activeCollKey, 'new')) },
|
||||
{ icon: 'edit', fn: evt => contextMenu.show(evt, buildMenu(activeHostKey, activeDbKey, activeCollKey, 'edit')), disabled: !activeHostKey },
|
||||
{ icon: '-', fn: evt => contextMenu.show(evt, buildMenu(activeHostKey, activeDbKey, activeCollKey, 'drop')), disabled: !activeDbKey },
|
||||
]}
|
||||
bind:activePath={activeGridPath}
|
||||
on:select={e => {
|
||||
const key = e.detail.itemKey;
|
||||
switch (e.detail?.level) {
|
||||
case 0: return openConnection(key);
|
||||
case 1: return openDatabase(key);
|
||||
case 2: return openCollection(key);
|
||||
}
|
||||
}}
|
||||
/>
|
@ -1,34 +1,66 @@
|
||||
<script>
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { Hosts } from '../../../wailsjs/go/app/App';
|
||||
import { onMount } from 'svelte';
|
||||
import { Hosts, RenameCollection } from '../../../wailsjs/go/app/App';
|
||||
import { input } from '../../actions';
|
||||
import Modal from '../../components/modal.svelte';
|
||||
import DatabaseList from './dblist.svelte';
|
||||
import HostTree from './hosttree.svelte';
|
||||
import { busy, connections } from '../../stores';
|
||||
import CollectionDetail from './collection/index.svelte';
|
||||
import HostDetail from './hostdetail.svelte';
|
||||
import Icon from '../../components/icon.svelte';
|
||||
|
||||
export let hosts = {};
|
||||
export let activeHostKey = '';
|
||||
export let activeDbKey = '';
|
||||
export let activeCollKey = '';
|
||||
|
||||
let addressBarModalOpen = true;
|
||||
let dbList;
|
||||
|
||||
let hostTree;
|
||||
let newDb;
|
||||
let newDbInput;
|
||||
let newColl;
|
||||
let newCollInput;
|
||||
|
||||
$: if (newDb) {
|
||||
tick().then(() => newDbInput.focus());
|
||||
let showHostDetail = false;
|
||||
let hostDetailKey = '';
|
||||
|
||||
let collToRename = '';
|
||||
let newCollKey = '';
|
||||
|
||||
async function getHosts() {
|
||||
hosts = await Hosts();
|
||||
}
|
||||
|
||||
function createHost() {
|
||||
hostDetailKey = '';
|
||||
showHostDetail = true;
|
||||
}
|
||||
|
||||
function editHost(hostKey) {
|
||||
hostDetailKey = hostKey;
|
||||
showHostDetail = true;
|
||||
}
|
||||
|
||||
async function createDatabase() {
|
||||
busy.start();
|
||||
$connections[activeHostKey].databases[newDb.name] = { collections: {} };
|
||||
newDb = undefined;
|
||||
await dbList.reload();
|
||||
await hostTree.reload();
|
||||
busy.end();
|
||||
}
|
||||
|
||||
function openEditCollModal(collKey) {
|
||||
newCollKey = collKey;
|
||||
collToRename = collKey;
|
||||
}
|
||||
|
||||
async function renameCollection() {
|
||||
busy.start();
|
||||
console.log(newCollKey);
|
||||
const ok = await RenameCollection(activeHostKey, activeDbKey, collToRename, newCollKey);
|
||||
if (ok) {
|
||||
activeCollKey = newCollKey;
|
||||
collToRename = '';
|
||||
newCollKey = '';
|
||||
await hostTree.reload();
|
||||
}
|
||||
busy.end();
|
||||
}
|
||||
|
||||
@ -36,24 +68,24 @@
|
||||
busy.start();
|
||||
$connections[activeHostKey].databases[activeDbKey].collections[newColl.name] = {};
|
||||
newColl = undefined;
|
||||
await dbList.reload();
|
||||
await hostTree.reload();
|
||||
busy.end();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
Hosts().then(h => hosts = h);
|
||||
});
|
||||
onMount(getHosts);
|
||||
</script>
|
||||
|
||||
<DatabaseList
|
||||
<HostTree
|
||||
{hosts}
|
||||
bind:activeHostKey
|
||||
bind:activeCollKey
|
||||
bind:activeDbKey
|
||||
bind:this={dbList}
|
||||
on:connected={() => addressBarModalOpen = false}
|
||||
bind:this={hostTree}
|
||||
on:newHost={createHost}
|
||||
on:newDatabase={() => newDb = {}}
|
||||
on:newCollection={() => newColl = {}}
|
||||
on:editHost={e => editHost(e.detail)}
|
||||
on:editCollection={e => openEditCollModal(e.detail)}
|
||||
/>
|
||||
|
||||
<CollectionDetail
|
||||
@ -63,13 +95,20 @@
|
||||
collectionKey={activeCollKey}
|
||||
/>
|
||||
|
||||
<HostDetail
|
||||
bind:show={showHostDetail}
|
||||
on:reload={getHosts}
|
||||
hostKey={activeHostKey}
|
||||
{hosts}
|
||||
/>
|
||||
|
||||
{#if newDb}
|
||||
<Modal bind:show={newDb}>
|
||||
<p><strong>Create a database</strong></p>
|
||||
<p>Note: databases in MongoDB do not exist until they have a collection and an item. Your new database will not persist on the server; fill it to have it created.</p>
|
||||
<form on:submit|preventDefault={createDatabase}>
|
||||
<label class="field">
|
||||
<input type="text" spellcheck="false" bind:value={newDb.name} use:input placeholder="New collection name" bind:this={newDbInput} />
|
||||
<input type="text" spellcheck="false" bind:value={newDb.name} use:input={{ autofocus: true }} placeholder="New collection name" />
|
||||
</label>
|
||||
<p class="modal-actions">
|
||||
<button class="btn create" type="submit" disabled={!newDb.name?.trim()}>Create database</button>
|
||||
@ -81,11 +120,11 @@
|
||||
|
||||
{#if newColl}
|
||||
<Modal bind:show={newColl}>
|
||||
<p><strong>Create a collections</strong></p>
|
||||
<p><strong>Create a collection</strong></p>
|
||||
<p>Note: collections in MongoDB do not exist until they have at least one item. Your new collection will not persist on the server; fill it to have it created.</p>
|
||||
<form on:submit|preventDefault={createCollection}>
|
||||
<label class="field">
|
||||
<input type="text" spellcheck="false" bind:value={newColl.name} use:input placeholder="New collection name" bind:this={newCollInput} />
|
||||
<input type="text" spellcheck="false" bind:value={newColl.name} use:input={{ autofocus: true }} placeholder="New collection name" />
|
||||
</label>
|
||||
<p class="modal-actions">
|
||||
<button class="btn create" type="submit" disabled={!newColl.name?.trim()}>Create collection</button>
|
||||
@ -95,9 +134,45 @@
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
{#if collToRename}
|
||||
<Modal bind:show={collToRename} width="400px">
|
||||
<form class="rename" on:submit|preventDefault={renameCollection}>
|
||||
<div>Renaming collection <strong>{collToRename}</strong></div>
|
||||
<Icon name="arr-d" />
|
||||
<div class="inputs">
|
||||
<button class="btn secondary" type="button" on:click={() => collToRename = ''}>Cancel</button>
|
||||
<label class="field">
|
||||
<input type="text" bind:value={newCollKey} use:input={{ autofocus: true }} spellcheck="false" />
|
||||
</label>
|
||||
<button class="btn" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.rename {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
.rename input {
|
||||
text-align: center;
|
||||
}
|
||||
.rename strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
.rename .inputs {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
grid-template: 1fr / auto 1fr auto;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -25,7 +25,7 @@ export const contextMenu = (() => {
|
||||
const { set, subscribe } = writable();
|
||||
|
||||
return {
|
||||
show: (evt, menu) => set(menu ? {
|
||||
show: (evt, menu) => set(Object.keys(menu || {}).length ? {
|
||||
position: [ evt.clientX, evt.clientY ],
|
||||
items: menu,
|
||||
} : undefined),
|
||||
|
@ -19,7 +19,6 @@ body {
|
||||
}
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
overscroll-behavior: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
2
frontend/wailsjs/go/app/App.d.ts
vendored
2
frontend/wailsjs/go/app/App.d.ts
vendored
@ -37,6 +37,8 @@ export function RemoveItemById(arg1:string,arg2:string,arg3:string,arg4:string):
|
||||
|
||||
export function RemoveItems(arg1:string,arg2:string,arg3:string,arg4:string,arg5:boolean):Promise<number>;
|
||||
|
||||
export function RenameCollection(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>;
|
||||
|
||||
export function Settings():Promise<app.Settings>;
|
||||
|
||||
export function UpdateHost(arg1:string,arg2:string):Promise<void>;
|
||||
|
@ -66,6 +66,10 @@ export function RemoveItems(arg1, arg2, arg3, arg4, arg5) {
|
||||
return window['go']['app']['App']['RemoveItems'](arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
export function RenameCollection(arg1, arg2, arg3, arg4) {
|
||||
return window['go']['app']['App']['RenameCollection'](arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
export function Settings() {
|
||||
return window['go']['app']['App']['Settings']();
|
||||
}
|
||||
|
@ -36,10 +36,10 @@ func (a *App) Menu() *menu.Menu {
|
||||
wailsRuntime.Quit(a.ctx)
|
||||
})
|
||||
|
||||
fileMenu := appMenu.AddSubmenu("File")
|
||||
fileMenu.AddText("Hosts…", keys.CmdOrCtrl("k"), func(cd *menu.CallbackData) {
|
||||
wailsRuntime.EventsEmit(a.ctx, "OpenHostsModal")
|
||||
})
|
||||
// fileMenu := appMenu.AddSubmenu("File")
|
||||
// fileMenu.AddText("Hosts…", keys.CmdOrCtrl("k"), func(cd *menu.CallbackData) {
|
||||
// wailsRuntime.EventsEmit(a.ctx, "OpenHostsModal")
|
||||
// })
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
appMenu.Append(menu.EditMenu())
|
||||
|
@ -13,21 +13,50 @@ func (a *App) OpenCollection(hostKey, dbKey, collKey string) (result bson.M) {
|
||||
fmt.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
command := bson.M{"collStats": collKey}
|
||||
err = client.Database(dbKey).RunCommand(ctx, command).Decode(&result)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.ErrorDialog,
|
||||
Title: "Could not retrieve collection list for " + dbKey,
|
||||
Title: "Could not retrieve collection stats for " + collKey,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
defer close()
|
||||
return result
|
||||
}
|
||||
|
||||
func (a *App) RenameCollection(hostKey, dbKey, collKey, newCollKey string) bool {
|
||||
client, ctx, close, err := a.connectToHost(hostKey)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
var result bson.M
|
||||
command := bson.D{
|
||||
bson.E{Key: "renameCollection", Value: fmt.Sprintf("%v.%v", dbKey, collKey)},
|
||||
bson.E{Key: "to", Value: fmt.Sprintf("%v.%v", dbKey, newCollKey)},
|
||||
}
|
||||
err = client.Database("admin").RunCommand(ctx, command).Decode(&result)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.ErrorDialog,
|
||||
Title: "Could not rename " + collKey,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
defer close()
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *App) DropCollection(hostKey, dbKey, collKey string) bool {
|
||||
sure, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Title: "Confirm",
|
||||
@ -50,7 +79,7 @@ func (a *App) DropCollection(hostKey, dbKey, collKey string) bool {
|
||||
fmt.Println(err.Error())
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.ErrorDialog,
|
||||
Title: "Could not drop " + dbKey,
|
||||
Title: "Could not drop " + collKey,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return false
|
||||
|
@ -1,12 +1,57 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
mongoOptions "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func (a *App) connectToHost(hostKey string) (*mongo.Client, context.Context, func(), error) {
|
||||
hosts, err := a.Hosts()
|
||||
if err != nil {
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.InfoDialog,
|
||||
Title: "Could not retrieve hosts",
|
||||
})
|
||||
return nil, nil, nil, errors.New("could not retrieve hosts")
|
||||
}
|
||||
|
||||
h := hosts[hostKey]
|
||||
if len(h.URI) == 0 {
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.InfoDialog,
|
||||
Title: "Invalid uri",
|
||||
Message: "You haven't specified a valid uri for the selected host.",
|
||||
})
|
||||
return nil, nil, nil, errors.New("invalid uri")
|
||||
}
|
||||
|
||||
client, err := mongo.NewClient(mongoOptions.Client().ApplyURI(h.URI))
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.ErrorDialog,
|
||||
Title: "Could not connect to " + h.Name,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return nil, nil, nil, errors.New("could not establish a connection with " + h.Name)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
client.Connect(ctx)
|
||||
return client, ctx, func() {
|
||||
client.Disconnect(ctx)
|
||||
cancel()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *App) OpenConnection(hostKey string) (databases []string) {
|
||||
client, ctx, close, err := a.connectToHost(hostKey)
|
||||
if err != nil {
|
||||
|
@ -13,6 +13,7 @@ func (a *App) OpenDatabase(hostKey, dbKey string) (collections []string) {
|
||||
fmt.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
collections, err = client.Database(dbKey).ListCollectionNames(ctx, bson.D{})
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
@ -23,6 +24,7 @@ func (a *App) OpenDatabase(hostKey, dbKey string) (collections []string) {
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
defer close()
|
||||
return collections
|
||||
}
|
||||
@ -44,6 +46,7 @@ func (a *App) DropDatabase(hostKey, dbKey string) bool {
|
||||
fmt.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
err = client.Database(dbKey).Drop(ctx)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
@ -54,6 +57,7 @@ func (a *App) DropDatabase(hostKey, dbKey string) bool {
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
defer close()
|
||||
return true
|
||||
}
|
||||
|
@ -1,18 +1,14 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
mongoOptions "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Host struct {
|
||||
@ -183,43 +179,3 @@ func (a *App) RemoveHost(key string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) connectToHost(hostKey string) (*mongo.Client, context.Context, func(), error) {
|
||||
hosts, err := a.Hosts()
|
||||
if err != nil {
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.InfoDialog,
|
||||
Title: "Could not retrieve hosts",
|
||||
})
|
||||
return nil, nil, nil, errors.New("could not retrieve hosts")
|
||||
}
|
||||
|
||||
h := hosts[hostKey]
|
||||
if len(h.URI) == 0 {
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.InfoDialog,
|
||||
Title: "Invalid uri",
|
||||
Message: "You haven't specified a valid uri for the selected host.",
|
||||
})
|
||||
return nil, nil, nil, errors.New("invalid uri")
|
||||
}
|
||||
|
||||
client, err := mongo.NewClient(mongoOptions.Client().ApplyURI(h.URI))
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.ErrorDialog,
|
||||
Title: "Could not connect to " + h.Name,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return nil, nil, nil, errors.New("could not establish a connection with " + h.Name)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
client.Connect(ctx)
|
||||
return client, ctx, func() {
|
||||
client.Disconnect(ctx)
|
||||
cancel()
|
||||
}, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user