mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-23 20:54:50 +03:00
add basic views into stacks
this adds a way to view the timeline, branches and stacks, create stacks and more. none of this is pretty, but we're just exercising the server APIs
This commit is contained in:
parent
4c11a96456
commit
d2444de8c5
@ -23,6 +23,9 @@
|
||||
"vite": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/sveltekit": "^8.9.2"
|
||||
"@sentry/sveltekit": "^8.9.2",
|
||||
"highlight.js": "^11.10.0",
|
||||
"marked": "^10.0.0",
|
||||
"moment": "^2.30.1"
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,13 @@
|
||||
<h2>GitButler</h2>
|
||||
</a>
|
||||
<div>
|
||||
<a href="/user">User</a>
|
||||
|
|
||||
<a href="/downloads">Downloads</a>
|
||||
{#if $token}
|
||||
|
|
||||
<a href="/projects">Projects</a>
|
||||
|
|
||||
<a href="/user">User</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="nav__right">
|
||||
<button
|
||||
|
@ -52,11 +52,13 @@ a:hover {
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1rem;
|
||||
font-size: 1.8rem;
|
||||
margin: 0;
|
||||
margin-bottom: 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
<div class="app">
|
||||
<Navigation />
|
||||
|
||||
<main>
|
||||
{@render children()}
|
||||
</main>
|
||||
@ -53,7 +54,7 @@
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
max-width: 64rem;
|
||||
max-width: 84rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
52
apps/web/src/routes/projects/+page.svelte
Normal file
52
apps/web/src/routes/projects/+page.svelte
Normal file
@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
import moment from 'moment';
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
let state = 'loading';
|
||||
let projects: any = {};
|
||||
|
||||
onMount(() => {
|
||||
let key = localStorage.getItem('gb_access_token');
|
||||
if (key) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/projects', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
projects = data;
|
||||
state = 'loaded';
|
||||
setTimeout(() => {
|
||||
let dtime = document.querySelectorAll('.dtime');
|
||||
dtime.forEach((element) => {
|
||||
console.log(element.innerHTML);
|
||||
element.innerHTML = moment(element.innerHTML).fromNow();
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
state = 'unauthorized';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if state === 'loading'}
|
||||
<p>Loading...</p>
|
||||
{:else if state === 'unauthorized'}
|
||||
<p>Unauthorized</p>
|
||||
{:else}
|
||||
{#each projects as project}
|
||||
<div>
|
||||
<h2><a href="/projects/{project.repository_id}">{project.name}</a></h2>
|
||||
<p>{project.repository_id}</p>
|
||||
<p>{project.description}</p>
|
||||
<p>Created: <span class="dtime">{project.created_at}</span></p>
|
||||
<p>Updated: <span class="dtime">{project.updated_at}</span></p>
|
||||
</div>
|
||||
<hr />
|
||||
{/each}
|
||||
{/if}
|
157
apps/web/src/routes/projects/[projectId]/+page.svelte
Normal file
157
apps/web/src/routes/projects/[projectId]/+page.svelte
Normal file
@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
let state = 'loading';
|
||||
let timeline: any = {};
|
||||
let patchStacks: any = {};
|
||||
let project: any = {};
|
||||
let projectId: string;
|
||||
|
||||
export let data: any;
|
||||
|
||||
function createPatchStack(branch: string, sha: string) {
|
||||
let key = localStorage.getItem('gb_access_token');
|
||||
let opts = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || '',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
project_id: projectId,
|
||||
branch_id: branch,
|
||||
oplog_sha: sha
|
||||
})
|
||||
};
|
||||
if (key) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/patch_stack', opts)
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
goto('/projects/' + projectId + '/branches/' + data.branch_id + '/stack');
|
||||
});
|
||||
} else {
|
||||
state = 'unauthorized';
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
let key = localStorage.getItem('gb_access_token');
|
||||
projectId = data.projectId;
|
||||
console.log(projectId);
|
||||
if (key) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/projects/' + projectId, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
project = data;
|
||||
state = 'loaded';
|
||||
});
|
||||
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/patch_stack/' + projectId, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
patchStacks = data;
|
||||
});
|
||||
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/timeline/' + projectId, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
timeline = data;
|
||||
state = 'loaded';
|
||||
});
|
||||
} else {
|
||||
state = 'unauthorized';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if state === 'loading'}
|
||||
<p>Loading...</p>
|
||||
{:else if state === 'unauthorized'}
|
||||
<p>Unauthorized</p>
|
||||
{:else}
|
||||
<h2>Project</h2>
|
||||
<div>{project.name}</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h2>Patch Stacks</h2>
|
||||
{#each patchStacks as stack}
|
||||
<div>
|
||||
{stack.title}<br />
|
||||
<a href="/projects/{data.projectId}/branches/{stack.branch_id}/stack">{stack.branch_id}</a
|
||||
><br />
|
||||
{stack.stack_size} patches<br />
|
||||
{stack.contributors}<br />
|
||||
v{stack.version}<br />
|
||||
{stack.created_at}<br />
|
||||
</div>
|
||||
<hr />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2>Timeline</h2>
|
||||
{#each timeline as event}
|
||||
<div class="event">
|
||||
<pre>{event.sha}</pre>
|
||||
<div>{event.time}</div>
|
||||
<pre>{event.message}</pre>
|
||||
<div>{event.trailers}</div>
|
||||
{#if Object.keys(event.files).length > 0}
|
||||
<h3>Branches</h3>
|
||||
{#each Object.keys(event.files) as branch}
|
||||
{#if event.branch_data.branches[branch]}
|
||||
<h3>Branch {event.branch_data.branches[branch].name}</h3>
|
||||
<button on:click={() => createPatchStack(branch, event.sha)}
|
||||
>Create Patch Stack</button
|
||||
>
|
||||
{#each Object.keys(event.files[branch]) as file}
|
||||
<li>{file} ({event.files[branch][file]})</li>
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
<hr />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
h2 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.event {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.columns {
|
||||
display: flex;
|
||||
}
|
||||
.column {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
3
apps/web/src/routes/projects/[projectId]/+page.ts
Normal file
3
apps/web/src/routes/projects/[projectId]/+page.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const load: any = ({ params }) => {
|
||||
return { projectId: params.projectId };
|
||||
};
|
@ -0,0 +1,122 @@
|
||||
<script lang="ts">
|
||||
import moment from 'moment';
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
// load moment
|
||||
let state = 'loading';
|
||||
let stackData: any = {};
|
||||
|
||||
export let data: any;
|
||||
|
||||
onMount(() => {
|
||||
let key = localStorage.getItem('gb_access_token');
|
||||
let projectId = data.projectId;
|
||||
let branchId = data.branchId;
|
||||
|
||||
if (key) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/patch_stack/' + projectId + '/' + branchId, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
stackData = data;
|
||||
state = 'loaded';
|
||||
// moment all the .dtime elements
|
||||
// wait a second
|
||||
setTimeout(() => {
|
||||
let dtime = document.querySelectorAll('.dtime');
|
||||
dtime.forEach((element) => {
|
||||
console.log(element.innerHTML);
|
||||
element.innerHTML = moment(element.innerHTML).fromNow();
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
state = 'unauthorized';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if state === 'loading'}
|
||||
<p>Loading...</p>
|
||||
{:else if state === 'unauthorized'}
|
||||
<p>Unauthorized</p>
|
||||
{:else}
|
||||
<div><a href="/projects/{data.projectId}">project</a></div>
|
||||
|
||||
<h1>Patch Stack</h1>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
Title: <strong>{stackData.title}</strong><br />
|
||||
Branch: <code>{stackData.branch_id}</code><br />
|
||||
Stack UUID: <code>{stackData.uuid}</code><br />
|
||||
Updated: <span class="dtime">{stackData.created_at}</span><br />
|
||||
</div>
|
||||
<div class="column">
|
||||
Stack Size: {stackData.stack_size}<br />
|
||||
{#if stackData.version > 1}
|
||||
Version: <a href="/projects/{data.projectId}/branches/{stackData.branch_id}/versions"
|
||||
>{stackData.version}</a
|
||||
><br />
|
||||
{:else}
|
||||
Version: {stackData.version}<br />
|
||||
{/if}
|
||||
Contributors: {stackData.contributors}<br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Patches</h2>
|
||||
{#each stackData.patches as patch}
|
||||
<div class="columns patch">
|
||||
<div class="column">
|
||||
<div>Title: <strong>{patch.title}</strong></div>
|
||||
<div>Change Id: <code><a href="./stack/{patch.change_id}">{patch.change_id}</a></code></div>
|
||||
<div>Commit: <code>{patch.commit_sha}</code></div>
|
||||
<div>Version: {patch.version}</div>
|
||||
<div><strong>Files:</strong></div>
|
||||
{#each patch.statistics.files as file}
|
||||
<div><code>{file}</code></div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="column">
|
||||
<div>Created: <span class="dtime">{patch.created_at}</span></div>
|
||||
<div>Contributors: {patch.contributors}</div>
|
||||
<div>
|
||||
Additions: {patch.statistics.lines - patch.statistics.deletions}, Deletions: {patch
|
||||
.statistics.deletions}, Files: {patch.statistics.file_count}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
hr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.columns {
|
||||
display: flex;
|
||||
}
|
||||
.column {
|
||||
flex: 1;
|
||||
}
|
||||
.column div {
|
||||
margin: 4px 0;
|
||||
}
|
||||
.patch {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
padding: 15px 20px;
|
||||
margin: 10px 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,6 @@
|
||||
export const load: any = ({ params }) => {
|
||||
return {
|
||||
projectId: params.projectId,
|
||||
branchId: params.branchId
|
||||
};
|
||||
};
|
@ -0,0 +1,371 @@
|
||||
<script lang="ts">
|
||||
import hljs from 'highlight.js';
|
||||
import { marked } from 'marked';
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
let state = 'loading';
|
||||
let patch: any = {};
|
||||
let stack: any = {};
|
||||
let key: any = '';
|
||||
let uuid: any = '';
|
||||
|
||||
export let data: any;
|
||||
|
||||
onMount(() => {
|
||||
key = localStorage.getItem('gb_access_token');
|
||||
let projectId = data.projectId;
|
||||
let branchId = data.branchId;
|
||||
let changeId = data.changeId;
|
||||
|
||||
if (key) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/patch_stack/' + projectId + '/' + branchId, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
stack = data;
|
||||
uuid = data.uuid;
|
||||
fetchPatch(data.uuid, changeId, key);
|
||||
});
|
||||
} else {
|
||||
state = 'unauthorized';
|
||||
}
|
||||
});
|
||||
|
||||
function fetchPatch(uuid: string, changeId: string, key: string) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/patch_stack/' + uuid + '/patch/' + changeId, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
})
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
patch = data;
|
||||
state = 'loaded';
|
||||
// wait a second
|
||||
setTimeout(() => {
|
||||
console.log('Highlighting code');
|
||||
hljs.highlightAll();
|
||||
// render markdowns
|
||||
let markdowns = document.querySelectorAll('.markdown');
|
||||
markdowns.forEach((markdown) => {
|
||||
markdown.innerHTML = marked(markdown.innerHTML);
|
||||
});
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
|
||||
function createSectionPost(position: number) {
|
||||
let opts = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || '',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'text',
|
||||
text: '# new section',
|
||||
position: position - 1
|
||||
})
|
||||
};
|
||||
if (key) {
|
||||
fetch(
|
||||
env.PUBLIC_APP_HOST + 'api/patch_stack/' + uuid + '/patch/' + data.changeId + '/section',
|
||||
opts
|
||||
)
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSectionPost(code: string) {
|
||||
let opts = {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || '',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
if (key) {
|
||||
fetch(
|
||||
env.PUBLIC_APP_HOST +
|
||||
'api/patch_stack/' +
|
||||
uuid +
|
||||
'/patch/' +
|
||||
data.changeId +
|
||||
'/section/' +
|
||||
code,
|
||||
opts
|
||||
)
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSection(code: string) {
|
||||
console.log('Adding section at position', code);
|
||||
deleteSectionPost(code);
|
||||
updatePatch();
|
||||
}
|
||||
|
||||
function addSection(position: number) {
|
||||
console.log('Adding section at position', position);
|
||||
createSectionPost(position);
|
||||
updatePatch();
|
||||
}
|
||||
|
||||
function orderSectionPatch(order: Array) {
|
||||
let opts = {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || '',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
section_order: order
|
||||
})
|
||||
};
|
||||
if (key) {
|
||||
fetch(env.PUBLIC_APP_HOST + 'api/patch_stack/' + uuid + '/patch/' + data.changeId, opts)
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
function moveSection(position: number, change: number) {
|
||||
console.log('Moving section at position', position, 'by', change);
|
||||
let ids = patch.sections.map((section: any) => section.identifier);
|
||||
// reorder ids array to move item in position to swap with item change off
|
||||
let temp = ids[position];
|
||||
ids[position] = ids[position + change];
|
||||
ids[position + change] = temp;
|
||||
// convert ids array to comma separated string
|
||||
orderSectionPatch(ids);
|
||||
console.log(ids);
|
||||
updatePatch();
|
||||
}
|
||||
|
||||
function editSection(code: string) {
|
||||
console.log('Editing section', code);
|
||||
let editor = document.querySelector('.edit-' + code);
|
||||
if (editor) {
|
||||
editor.style.display = 'block';
|
||||
let display = document.querySelector('.display-' + code);
|
||||
if (display) {
|
||||
display.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveSection(code: string) {
|
||||
console.log('Saving section', code);
|
||||
let editor = document.querySelector('.edit-' + code);
|
||||
if (editor) {
|
||||
let text = editor.querySelector('textarea').value;
|
||||
let opts = {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || '',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text: text
|
||||
})
|
||||
};
|
||||
if (key) {
|
||||
fetch(
|
||||
env.PUBLIC_APP_HOST +
|
||||
'api/patch_stack/' +
|
||||
uuid +
|
||||
'/patch/' +
|
||||
data.changeId +
|
||||
'/section/' +
|
||||
code,
|
||||
opts
|
||||
)
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
editor.style.display = 'none';
|
||||
let display = document.querySelector('.display-' + code);
|
||||
if (display) {
|
||||
display.style.display = 'block';
|
||||
display.innerHTML = text;
|
||||
updatePatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
function updatePatch() {
|
||||
setTimeout(() => {
|
||||
fetchPatch(uuid, data.changeId, key);
|
||||
}, 500);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if state === 'loading'}
|
||||
<p>Loading...</p>
|
||||
{:else if state === 'unauthorized'}
|
||||
<p>Unauthorized</p>
|
||||
{:else}
|
||||
<h2>Patch Stack: <a href="../stack">{stack.title}</a></h2>
|
||||
{#each stack.patches as stackPatch}
|
||||
<div>
|
||||
<code
|
||||
><a href="/projects/{data.projectId}/branches/{data.branchId}/stack/{stackPatch.change_id}"
|
||||
>{stackPatch.change_id.substr(0, 8)}</a
|
||||
></code
|
||||
>:
|
||||
{#if patch.change_id === stackPatch.change_id}
|
||||
<strong>{stackPatch.title}</strong>
|
||||
{:else}
|
||||
{stackPatch.title}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
<hr />
|
||||
|
||||
<h2>Patch</h2>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div>Title: <strong>{patch.title}</strong></div>
|
||||
{#if patch.description}
|
||||
<div>Desc: {patch.description}</div>
|
||||
{/if}
|
||||
<div>Change Id: <code>{patch.change_id}</code></div>
|
||||
<div>Commit: <code>{patch.commit_sha}</code></div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div>Patch Version: {patch.version}</div>
|
||||
<div>Stack Position: {patch.position + 1}/{stack.stack_size}</div>
|
||||
<div>Contributors: {patch.contributors}</div>
|
||||
<div>
|
||||
Additions: {patch.statistics.lines - patch.statistics.deletions}, Deletions: {patch
|
||||
.statistics.deletions}, Files: {patch.statistics.file_count}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column outline">
|
||||
<h3>Outline</h3>
|
||||
<div class="sections">
|
||||
{#each patch.sections as section}
|
||||
{#if section.section_type === 'diff'}
|
||||
<div><a href="#section-{section.id}">{section.new_path}</a></div>
|
||||
{:else}
|
||||
<div><a href="#section-{section.id}">{section.title}</a></div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="patch">
|
||||
{#each patch.sections as section}
|
||||
<div id="section-{section.id}">
|
||||
{#if section.section_type === 'diff'}
|
||||
<div class="right">
|
||||
<a class="action" on:click={() => addSection(section.position)}>add</a>
|
||||
[<a class="action" on:click={() => moveSection(section.position, -1)}>up</a>
|
||||
<a class="action" on:click={() => moveSection(section.position, 1)}>down</a>]
|
||||
</div>
|
||||
<div>
|
||||
<strong>{section.new_path}</strong>
|
||||
</div>
|
||||
<div><pre><code>{section.diff_patch}</code></pre></div>
|
||||
{:else}
|
||||
<div class="right">
|
||||
<a class="action" on:click={() => addSection(section.position)}>add</a>
|
||||
[
|
||||
<a class="action" on:click={() => editSection(section.code)}>edit</a>] [
|
||||
<a class="action" on:click={() => deleteSection(section.code)}>del</a>] [
|
||||
<a class="action" on:click={() => moveSection(section.position, -1)}>up</a>
|
||||
<a class="action" on:click={() => moveSection(section.position, 1)}>down</a>
|
||||
]
|
||||
</div>
|
||||
<div class="editor edit-{section.code}">
|
||||
<textarea class="editing">{section.data.text}</textarea>
|
||||
<button on:click={() => saveSection(section.code)}>Save</button>
|
||||
</div>
|
||||
<div class="markdown display-{section.code}">{section.data.text}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
<div class="right">
|
||||
<a class="action" on:click={() => addSection(patch.sections.length)}>add</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/default.min.css"
|
||||
/>
|
||||
|
||||
<style>
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
code {
|
||||
background-color: #f4f4f4;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
.columns {
|
||||
display: flex;
|
||||
}
|
||||
.column {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
.outline {
|
||||
max-width: 250px;
|
||||
}
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
gap: 5px;
|
||||
color: #888;
|
||||
}
|
||||
.action {
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
.sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
.editing {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
font-family: monospace;
|
||||
font-size: large;
|
||||
}
|
||||
.editor {
|
||||
display: none;
|
||||
}
|
||||
.patch {
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,7 @@
|
||||
export const load: any = ({ params }) => {
|
||||
return {
|
||||
projectId: params.projectId,
|
||||
branchId: params.branchId,
|
||||
changeId: params.changeId
|
||||
};
|
||||
};
|
@ -0,0 +1,117 @@
|
||||
<script lang="ts">
|
||||
import moment from 'moment';
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
// load moment
|
||||
let state = 'loading';
|
||||
let stackData: any = {};
|
||||
|
||||
export let data: any;
|
||||
|
||||
onMount(() => {
|
||||
let key = localStorage.getItem('gb_access_token');
|
||||
let projectId = data.projectId;
|
||||
let branchId = data.branchId;
|
||||
|
||||
if (key) {
|
||||
fetch(
|
||||
env.PUBLIC_APP_HOST +
|
||||
'api/patch_stack/' +
|
||||
projectId +
|
||||
'?branch_id=' +
|
||||
branchId +
|
||||
'&status=all',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-AUTH-TOKEN': key || ''
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(async (response) => await response.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
stackData = data;
|
||||
state = 'loaded';
|
||||
// moment all the .dtime elements
|
||||
// wait a second
|
||||
setTimeout(() => {
|
||||
let dtime = document.querySelectorAll('.dtime');
|
||||
dtime.forEach((element) => {
|
||||
console.log(element.innerHTML);
|
||||
element.innerHTML = moment(element.innerHTML).fromNow();
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
state = 'unauthorized';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if state === 'loading'}
|
||||
<p>Loading...</p>
|
||||
{:else if state === 'unauthorized'}
|
||||
<p>Unauthorized</p>
|
||||
{:else}
|
||||
<div><a href="/projects/{data.projectId}">project</a></div>
|
||||
|
||||
<h1>Stack History</h1>
|
||||
|
||||
{#each stackData as stack}
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
Title: <strong>{stack.title}</strong><br />
|
||||
Branch: <code>{stack.branch_id}</code><br />
|
||||
Stack UUID: <code>{stack.uuid}</code><br />
|
||||
Updated: <span class="dtime">{stack.created_at}</span><br />
|
||||
</div>
|
||||
<div class="column">
|
||||
Stack Size: {stack.stack_size}<br />
|
||||
Version: {stack.version}<br />
|
||||
Contributors: {stack.contributors}<br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Patches:
|
||||
<ul>
|
||||
{#each stack.patches as patch}
|
||||
<li>
|
||||
<code style="background-color:#{patch.change_id.substr(0, 6)}"
|
||||
>{patch.change_id.substr(0, 6)}</code
|
||||
>:
|
||||
<code style="background-color:#{patch.commit_sha.substr(0, 6)}" <code
|
||||
>{patch.commit_sha.substr(0, 6)}</code
|
||||
>: {patch.title} : v{patch.version}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<hr />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
hr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.columns {
|
||||
display: flex;
|
||||
}
|
||||
.column {
|
||||
flex: 1;
|
||||
}
|
||||
.column div {
|
||||
margin: 4px 0;
|
||||
}
|
||||
.patch {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
padding: 15px 20px;
|
||||
margin: 10px 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,6 @@
|
||||
export const load: any = ({ params }) => {
|
||||
return {
|
||||
projectId: params.projectId,
|
||||
branchId: params.branchId
|
||||
};
|
||||
};
|
@ -4,28 +4,6 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
catalogs:
|
||||
default:
|
||||
vite:
|
||||
specifier: 5.2.13
|
||||
version: 5.2.13
|
||||
svelte:
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: 3.0.4
|
||||
version: 3.0.4
|
||||
'@sveltejs/kit':
|
||||
specifier: 2.5.25
|
||||
version: 2.5.25
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: 4.0.0-next.6
|
||||
version: 4.0.0-next.6
|
||||
svelte:
|
||||
specifier: 5.0.0-next.243
|
||||
version: 5.0.0-next.243
|
||||
svelte-check:
|
||||
specifier: 4.0.1
|
||||
version: 4.0.1
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@ -295,6 +273,15 @@ importers:
|
||||
'@sentry/sveltekit':
|
||||
specifier: ^8.9.2
|
||||
version: 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)(@sveltejs/kit@2.5.25(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@5.0.0-next.243)(vite@5.2.13(@types/node@22.3.0)))(svelte@5.0.0-next.243)(vite@5.2.13(@types/node@22.3.0)))(svelte@5.0.0-next.243)
|
||||
highlight.js:
|
||||
specifier: ^11.10.0
|
||||
version: 11.10.0
|
||||
marked:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
devDependencies:
|
||||
'@fontsource/fira-mono':
|
||||
specifier: ^4.5.10
|
||||
@ -4130,6 +4117,10 @@ packages:
|
||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||
hasBin: true
|
||||
|
||||
highlight.js@11.10.0:
|
||||
resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
hosted-git-info@7.0.2:
|
||||
resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
|
||||
engines: {node: ^16.14.0 || >=18.0.0}
|
||||
@ -4848,6 +4839,9 @@ packages:
|
||||
module-details-from-path@1.0.3:
|
||||
resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
|
||||
|
||||
moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
|
||||
mri@1.2.0:
|
||||
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
|
||||
engines: {node: '>=4'}
|
||||
@ -11385,6 +11379,8 @@ snapshots:
|
||||
|
||||
he@1.2.0: {}
|
||||
|
||||
highlight.js@11.10.0: {}
|
||||
|
||||
hosted-git-info@7.0.2:
|
||||
dependencies:
|
||||
lru-cache: 10.2.2
|
||||
@ -12101,6 +12097,8 @@ snapshots:
|
||||
|
||||
module-details-from-path@1.0.3: {}
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
||||
mri@1.2.0: {}
|
||||
|
||||
mrmime@2.0.0: {}
|
||||
|
Loading…
Reference in New Issue
Block a user