diff options
Diffstat (limited to 'src/frontend')
-rw-r--r-- | src/frontend/App.vue | 11 | ||||
-rw-r--r-- | src/frontend/app.html | 11 | ||||
-rw-r--r-- | src/frontend/app.js | 7 | ||||
-rw-r--r-- | src/frontend/components/BaseBackButton.vue | 25 | ||||
-rw-r--r-- | src/frontend/components/CommitPatch.vue | 149 | ||||
-rw-r--r-- | src/frontend/components/HomeHeader.vue | 39 | ||||
-rw-r--r-- | src/frontend/components/RepositoryHeader.vue | 50 | ||||
-rw-r--r-- | src/frontend/components/RepositoryNavbar.vue | 32 | ||||
-rw-r--r-- | src/frontend/router/index.js | 39 | ||||
-rw-r--r-- | src/frontend/scss/abstracts/_colors.scss | 9 | ||||
-rw-r--r-- | src/frontend/scss/abstracts/_fonts.scss | 5 | ||||
-rw-r--r-- | src/frontend/scss/style.scss | 284 | ||||
-rw-r--r-- | src/frontend/util/hljs-languages.js | 45 | ||||
-rw-r--r-- | src/frontend/views/Home.vue | 62 | ||||
-rw-r--r-- | src/frontend/views/Repository.vue | 29 | ||||
-rw-r--r-- | src/frontend/views/RepositoryCommit.vue | 98 | ||||
-rw-r--r-- | src/frontend/views/RepositoryLog.vue | 91 |
17 files changed, 986 insertions, 0 deletions
diff --git a/src/frontend/App.vue b/src/frontend/App.vue new file mode 100644 index 0000000..cbdce56 --- /dev/null +++ b/src/frontend/App.vue @@ -0,0 +1,11 @@ +<template> + <div id="container" class="container-fluid px-0"> + <router-view/> + </div> +</template> + +<script> +export default { + name: "App" +} +</script>
\ No newline at end of file diff --git a/src/frontend/app.html b/src/frontend/app.html new file mode 100644 index 0000000..348ca45 --- /dev/null +++ b/src/frontend/app.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" href="scss/style.scss"> + </head> + <body> + <div id="app"></div> + <script src="app.js"></script> + </body> +</html>
\ No newline at end of file diff --git a/src/frontend/app.js b/src/frontend/app.js new file mode 100644 index 0000000..1b43bbe --- /dev/null +++ b/src/frontend/app.js @@ -0,0 +1,7 @@ +import { createApp } from "vue/dist/vue.esm-bundler"; +import App from "./App.vue"; +import router from "./router"; + +createApp(App) + .use(router) + .mount("#app");
\ No newline at end of file diff --git a/src/frontend/components/BaseBackButton.vue b/src/frontend/components/BaseBackButton.vue new file mode 100644 index 0000000..64b1286 --- /dev/null +++ b/src/frontend/components/BaseBackButton.vue @@ -0,0 +1,25 @@ +<template> + <div class="d-inline"> + <router-link :to="to"> + <svg + xmlns="http://www.w3.org/2000/svg" id="back" + height="24px" width="24px" + viewBox="0 0 24 24" fill="#ffffff"> + <path d="M0 0h24v24H0z" fill="none" /> + <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /> + </svg> + </router-link> + </div> +</template> + +<script> +export default { + name: "BaseBackButton", + props: { + to: { + type: String, + required: true + } + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/components/CommitPatch.vue b/src/frontend/components/CommitPatch.vue new file mode 100644 index 0000000..53edeb9 --- /dev/null +++ b/src/frontend/components/CommitPatch.vue @@ -0,0 +1,149 @@ +<script> +import { h } from "vue"; +import hljs from "highlight.js"; +import hljs_languages from "../util/hljs-languages"; + +export default { + name: "CommitPatch", + props: { + patch: { + type: Object, + required: true + } + }, + setup(props) + { + const commit_patch = [ + h("div", { "class": "commit-file-header" }, [ + h("span", { "class": "fw-bold"}, (props.patch["to"] === "/dev/null") ? props.patch["from"] : props.patch["to"]), + h("span", (props.patch["to"] === "/dev/null") ? "Deleted" : "" ), + h("div", { class: "commit-file-add-del" }, [ + h("span", `+${ props.patch["additions"] }`), + h("span", `-${ props.patch["deletions"] }`) + ]) + ]) + ]; + + if(props.patch["too_large"] === false) { + let all_hunks = props.patch["hunks"].map((hunk) => hunk["hunk"]); + + const language = hljs_languages.find((lang) => lang["extensions"].some((extension) => props.patch["to"].endsWith(extension))); + let highlighted = language ? hljs.highlight(language["name"], all_hunks.join("\n")) : hljs.highlightAuto(all_hunks.join("\n")); + console.log(highlighted); + highlighted = highlighted["value"].split("\n"); + + const highlighted_hunks = []; + let hunk_start = 0; + all_hunks.forEach((hunk) => + { + const hunk_row_cnt = hunk.split("\n").length; + highlighted_hunks.push(highlighted.slice(hunk_start, hunk_start + hunk_row_cnt)); + hunk_start = hunk_start + hunk_row_cnt; + }); + + all_hunks = all_hunks.map((hunk) => hunk.split("\n")); + + commit_patch.push(h("table", { cellspacing: "0px" }, [ + h("tbody", [ + props.patch["hunks"].map((hunk, hunk_index) => + { + let new_offset = 0; + let deleted_offset = 0; + const multiline_comments = []; + + return highlighted_hunks[hunk_index].map((line, line_index) => + { + if(/^@@ -[0-9,]+ \+[0-9,]+ @@/.test(all_hunks[hunk_index][line_index])) { + new_offset++; + deleted_offset++; + return h("tr", { class: "commit-file-pos-change" }, [ + h("td", "..."), + h("td", "..."), + h("td", "..."), + h("td", [ + h("code", all_hunks[hunk_index][line_index]) + ]) + ]); + } + else if(/^\\ No newline at end of file$/.test(all_hunks[hunk_index][line_index])) { + new_offset++; + deleted_offset++; + return h("tr", { class: "commit-file-no-newline" }, [ + h("td", ""), + h("td", ""), + h("td", ""), + h("td", [ + h("code", all_hunks[hunk_index][line_index]) + ]) + ]); + } + else { + let first_td; + let second_td; + let third_td; + + if(hunk['new'].includes(line_index)) { + first_td = h("td", ""); + second_td = h("td", { class: "line-highlight-new" }, Number(hunk["new_start"]) + line_index - new_offset); + third_td = h("td", { class: "line-new" }, "+") + deleted_offset++; + } + else if(hunk['deleted'].includes(line_index)) { + first_td = h("td", Number(hunk["old_start"]) + line_index - deleted_offset); + second_td = h("td", { class: "line-highlight-deleted" }); + third_td = h("td", { class: "line-deleted" }, "-") + new_offset++; + } + else { + first_td = h("td", { class: "line-unchanged" }, Number(hunk["old_start"]) + line_index - deleted_offset); + second_td = h("td", { class: "line-unchanged" }, Number(hunk["new_start"]) + line_index - new_offset); + third_td = h("td", ""); + } + + let comment_open = line.match(/<span class="hljs-comment">/g); + const comment_open_cnt = (comment_open !== null) ? comment_open.length : 0; + comment_open = (comment_open !== null) ? comment_open[0] : ""; + + let comment_close = line.match(/<\/span>/g); + const comment_close_cnt = (comment_close !== null) ? comment_close.length : 0; + comment_close = (comment_close !== null) ? comment_close[0] : ""; + + if(comment_open_cnt > comment_close_cnt) { + line = line + "</span>"; + console.log("Öppning " + line); + multiline_comments.push(comment_open); + } + else if(comment_open_cnt < comment_close_cnt && multiline_comments.length !== 0) { + line = multiline_comments[multiline_comments.length - 1] + line; + console.log("Stängning " + line + " " + multiline_comments[multiline_comments.length - 1]); + multiline_comments.pop(); + } + else if(multiline_comments.length !== 0) { + line = multiline_comments[multiline_comments.length - 1] + line + "</span>"; + console.log("Mitt i " + line); + } + + return h("tr", [ + first_td, + second_td, + third_td, + h("td", [ + h("code", { innerHTML: line }) + ]) + ]); + } + }); + }) + ]) + ])); + } + else { + commit_patch.push(h("div", { class: "ps-3 pt-3 patch-too-large" }, [ + h("span", "Patch is too large to display.") + ])); + } + + return () => h("div", { class: "commit-file" }, commit_patch); + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/components/HomeHeader.vue b/src/frontend/components/HomeHeader.vue new file mode 100644 index 0000000..f0366a3 --- /dev/null +++ b/src/frontend/components/HomeHeader.vue @@ -0,0 +1,39 @@ +<template> + <div class="row mx-0"> + <div id="header" class="col d-flex mt-3 ms-2"> + <div class="d-inline ms-3"> + <span id="title" class="fs-1">{{ title }}</span> + <p id="about" class="mb-3 fs-4"> + {{ about }} + </p> + </div> + </div> + </div> +</template> + +<script> +import { watch, reactive, toRefs } from "vue"; + +export default { + name: "HomeHeader", + setup() + { + const state = reactive({ title: String, about: String }); + + watch(() => + { + fetch(`http://localhost:1337/api/v1/info`) + .then((res) => res.json()) + .then((data) => + { + state.title = data["data"]["title"], + state.about = data["data"]["about"] + }); + }); + + return { + ... toRefs(state) + }; + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryHeader.vue b/src/frontend/components/RepositoryHeader.vue new file mode 100644 index 0000000..b0db4f9 --- /dev/null +++ b/src/frontend/components/RepositoryHeader.vue @@ -0,0 +1,50 @@ +<template> + <div class="row mx-0"> + <div id="header" class="col d-flex mt-3 ms-2"> + <BaseBackButton to="/" /> + <div class="d-inline ms-3"> + <span id="title" class="fs-1">{{ title }}</span> + <p id="about" class="mb-3 fs-4"> + {{ about }} + </p> + </div> + </div> + </div> +</template> + +<script> +import BaseBackButton from "./BaseBackButton"; +import { watch, reactive, toRefs } from "vue"; + +export default { + name: "RepositoryHeader", + components: { + BaseBackButton + }, + props: { + repository: { + type: String, + required: true + } + }, + setup(props) + { + const state = reactive({ title: "", about: "" }); + + watch(() => + { + fetch(`http://localhost:1337/api/v1/repos/${props.repository}`) + .then((res) => res.json()) + .then((data) => + { + state.title = data["data"]["name"]; + state.about = data["data"]["description"]; + }); + }); + + return { + ... toRefs(state) + }; + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryNavbar.vue b/src/frontend/components/RepositoryNavbar.vue new file mode 100644 index 0000000..a1e1002 --- /dev/null +++ b/src/frontend/components/RepositoryNavbar.vue @@ -0,0 +1,32 @@ +<template> + <div id="navbar" class="row mx-0"> + <div id="repo-navbar" class="col ms-4 ps-4"> + <nav class="navbar navbar-expand navbar-dark"> + <div class="container-fluid px-0"> + <div class="collapse navbar-collapse"> + <ul class="navbar-nav"> + <li v-for="(item, index) in nav_items" v-bind:key="index" class="nav-item"> + <a class="nav-link fs-4" :class="{ active: activePage === item }" :aria-current="(activePage === item) ? 'page' : ''" :href="item">{{ item }}</a> + </li> + </ul> + </div> + </div> + </nav> + </div> + </div> +</template> + +<script> +export default { + name: "RepositoryNavbar", + props: { + activePage: String + }, + data() + { + return { + nav_items: ["log", "refs", "tree"] + }; + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/router/index.js b/src/frontend/router/index.js new file mode 100644 index 0000000..68762cd --- /dev/null +++ b/src/frontend/router/index.js @@ -0,0 +1,39 @@ +import { createRouter, createWebHistory } from "vue-router"; +import Home from "../views/Home"; +import Repository from "../views/Repository"; +import RepositoryLog from "../views/RepositoryLog"; +import RepositoryCommit from "../views/RepositoryCommit"; + +const routes = [ + { + name: "Home", + path: "/", + component: Home + }, + { + name: "Repository", + path: '/:repo([a-zA-Z0-9\\.\\-_]+)', + component: Repository, + props: route => ({ repository: route.params.repo }), + children: [ + { + name: "Repository Log", + path: "log", + component: RepositoryLog, + }, + { + name: "Commit", + path: "log/:commit([a-fA-F0-9]{40}$)", + component: RepositoryCommit, + props: route => ({ commit: route.params.commit }) + } + ] + } +] + +const router = createRouter({ + history: createWebHistory(process.env.BASE_URL), + routes +}); + +export default router;
\ No newline at end of file diff --git a/src/frontend/scss/abstracts/_colors.scss b/src/frontend/scss/abstracts/_colors.scss new file mode 100644 index 0000000..d7c43f5 --- /dev/null +++ b/src/frontend/scss/abstracts/_colors.scss @@ -0,0 +1,9 @@ +$primary: #023E8A; +$primary-light: #0096C7; +$secondary: #F48C06; +$success: #40916C; +$new: #06d6a0; +$danger: #D00000; +$text: #ffffff; +$text-gray: #6c757d; +$background: #121212;
\ No newline at end of file diff --git a/src/frontend/scss/abstracts/_fonts.scss b/src/frontend/scss/abstracts/_fonts.scss new file mode 100644 index 0000000..6af5233 --- /dev/null +++ b/src/frontend/scss/abstracts/_fonts.scss @@ -0,0 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300&display=swap'); + +$title: 'Oxygen', sans-serif; +$primary: 'Open Sans', sans-serif;
\ No newline at end of file diff --git a/src/frontend/scss/style.scss b/src/frontend/scss/style.scss new file mode 100644 index 0000000..77fee90 --- /dev/null +++ b/src/frontend/scss/style.scss @@ -0,0 +1,284 @@ +@use "abstracts/colors"; +@use "abstracts/fonts"; + +@import "../../../node_modules/bootstrap/scss/functions"; +@import "../../../node_modules/bootstrap/scss/variables"; +@import "../../../node_modules/bootstrap/scss/mixins"; + +$theme-colors: ( + "primary": colors.$primary, + "secondary": colors.$secondary, + "success": colors.$success, + "info": $info, + "warning": $warning, + "danger": colors.$danger, + "light": $light, + "dark": $dark +); + +$font-size-base: 0.75rem; + +$h1-font-size: $font-size-base * 2.5; +$h2-font-size: $font-size-base * 2; +$h3-font-size: $font-size-base * 1.75; +$h4-font-size: $font-size-base * 1.5; +$h5-font-size: $font-size-base * 1.125; +$h6-font-size: $font-size-base; + +$font-sizes: ( + 1: $h1-font-size, + 2: $h2-font-size, + 3: $h3-font-size, + 4: $h4-font-size, + 5: $h5-font-size, + 6: $h6-font-size +); + +$navbar-nav-link-padding-x: 0.5rem; + +@import "../../../node_modules/bootstrap/scss/breadcrumb"; +@import "../../../node_modules/bootstrap/scss/utilities"; +@import "../../../node_modules/bootstrap/scss/utilities/api"; +@import "../../../node_modules/bootstrap/scss/nav"; +@import "../../../node_modules/bootstrap/scss/navbar"; + +$table-cell-padding-x: 1rem; +$table-cell-padding-y: 0.2rem; + +$table-variants: ( + "primary": shift-color($primary, $table-bg-scale), + "secondary": shift-color($secondary, $table-bg-scale), + "success": shift-color($success, $table-bg-scale), + "info": shift-color($info, $table-bg-scale), + "warning": shift-color($warning, $table-bg-scale), + "danger": shift-color($danger, $table-bg-scale), + "light": $light, + "dark": colors.$background, +); + +@import "../../../node_modules/bootstrap/scss/tables"; + +@import "../../../node_modules/bootstrap/scss/containers"; +@import "../../../node_modules/bootstrap/scss/grid"; + +@import "../../../node_modules/highlight.js/scss/srcery.scss"; + +body { + background-color: colors.$background; + color: colors.$text; + font-family: fonts.$primary; + margin: 0px; +} + +ul { + list-style-type: none; + padding: 0; +} + +li { + margin-bottom: 25px; + div { + h2 { + margin: 0px; + } + } +} + +p { + margin: 0px; +} + +#title { + font-family: fonts.$title; + font-weight: 300; + line-height: 0.6; +} + +#about { + font-weight: 300; + padding-left: 1px; +} + +#projects-search { + align-items: center; + form { + display: flex; + align-items: center; + height: 35px; + input[type=search] { + margin-right: 15px; + } + } +} + +#repos { + margin-top: 25px; +} + +.repo-last-updated { + display: block; + font-weight: 300; + font-style: italic; +} + +input[type=submit] { + background-color: colors.$primary; + color: colors.$text; + font-size: 1rem; + border: 0px; + border-radius: 7px; + padding: 8px 15px 8px 15px; +} + +a { + color: colors.$text; + text-decoration: none; + &:hover { + color: colors.$primary-light; + } +} + +.breadcrumb { + li { + margin-bottom: 0.5rem; + } +} + +#commit-info { + margin-bottom: 2rem; + tbody tr { + td { + padding: 0px; + padding-right: 10px; + } + } +} + +.commit-file { + margin-bottom: 50px; + table { + padding-top: 15px; + tbody tr td { + padding: 0px; + padding-left: 8px; + vertical-align: top; + &:nth-child(2) { + padding-right: 7px; + } + &:nth-child(3) { + padding-right: 15px; + } + } + } +} + +.commit-file-add-del { + margin-left: auto; + margin-right: 23px; + span { + margin-right: 10px !important; + font-weight: 700; + &:nth-child(1) { + color: colors.$new; + } + } +} + +.commit-file-pos-change { + color: colors.$text-gray; +} + +.commit-file-no-newline { + color: colors.$text-gray; +} + +.line-new { + color: colors.$new; +} +.line-deleted { + color: colors.$danger; +} + +.line-unchanged { + color: colors.$text-gray; +} + +.line-highlight-new { + border-right: 1px solid colors.$new; +} +.line-highlight-deleted { + border-right: 1px solid colors.$danger; +} + +code { + white-space: pre-wrap; + word-wrap: anywhere; +} + +.commit-file-header { + display: flex; + background-color: rgba($color: #ffffff, $alpha: 0.08); + padding: 10px; + span { + margin-right: 30px; + &:nth-child(2) { + color: colors.$danger; + } + } +} + +#back:hover { + fill: colors.$primary-light; +} + +#navbar { + line-height: 0; +} + +th { + text-align: start; +} + +.commit-info-title { + color: colors.$secondary; + padding-right: 30px; + width: 20px; +} + +.patch-too-large { + font-weight: 600; +} + + +@include media-breakpoint-down(sm) { + .commit-file table tbody tr td { + padding-left: 4px; + &:nth-child(2) { + padding-right: 4px; + } + &:nth-child(3) { + padding-right: 5px; + } + } + .table > :not(caption) > * > * { + padding: 0.1rem; + } +} + +@media (max-width: 1200px) { + .fs-1 { + font-size: calc(1.375rem + 0.667vw) !important; + } + .fs-2 { + font-size: calc(1.325rem + 1.584vw) !important; + } + .fs-3 { + font-size: calc(1.3rem + 0.017vw) !important; + } + .fs-4 { + font-size: calc(0.82rem + 0.4vw) !important; + } + .fs-5 { + font-size: calc(0.65rem + 0.25vw) !important; + } +}
\ No newline at end of file diff --git a/src/frontend/util/hljs-languages.js b/src/frontend/util/hljs-languages.js new file mode 100644 index 0000000..c8576e0 --- /dev/null +++ b/src/frontend/util/hljs-languages.js @@ -0,0 +1,45 @@ +const languages = [ + { "name": "arduino", "extensions": [ ".ino" ]}, + { "name": "actionscript", "extensions": [ ".as" ]}, + { "name": "bash", "extensions": [ ".sh", ".zsh" ]}, + { "name": "csharp", "extensions": [ ".cs" ]}, + { "name": "c", "extensions": [ ".c", ".h" ]}, + { "name": "cpp", "extensions": [ ".cpp", ".hpp" ]}, + { "name": "cmake", "extensions": [ "cmake.in" ]}, + { "name": "css", "extensions": [ ".css" ]}, + { "name": "d", "extensions": [ ".d" ]}, + { "name": "dos", "extensions": [ ".bat", ".cmd" ]}, + { "name": "dockerfile", "extensions": [ "dockerfile", "Dockerfile" ]}, + { "name": "go", "extensions": [ ".go" ]}, + { "name": "gradle", "extensions": [ ".gradle" ]}, + { "name": "xml", "extensions": [ ".xml", ".html", ".xhtml", ".rss", ".atom", ".xjb", ".xsd", ".xsl", ".plist", ".svg" ]}, + { "name": "haskell", "extensions": [ ".hs" ]}, + { "name": "ini", "extensions": [ ".ini", ".toml" ]}, + { "name": "json", "extensions": [ ".json" ]}, + { "name": "java", "extensions": [ ".java", ".jsp" ]}, + { "name": "javascript", "extensions": [ ".js", ".jsx" ]}, + { "name": "kotlin", "extensions": [ ".kt" ]}, + { "name": "lua", "extensions": [ ".lua" ]}, + { "name": "makefile", "extensions": [ "makefile", "Makefile" ]}, + { "name": "markdown", "extensions": [ ".md" ]}, + { "name": "objectivec", "extensions": [ ".m", ".mm", ".M" ]}, + { "name": "php", "extensions": [ ".php" ]}, + { "name": "perl", "extensions": [ ".pl", ".pm" ]}, + { "name": "plaintext", "extensions": [ ".txt" ]}, + { "name": "pgsql", "extensions": [ ".pgsql" ]}, + { "name": "powershell", "extensions": [ ".ps", ".ps1" ]}, + { "name": "python", "extensions": [ ".py" ]}, + { "name": "ruby", "extensions": [ ".rb" ]}, + { "name": "rust", "extensions": [ ".rs" ]}, + { "name": "scss", "extensions": [ ".scss" ]}, + { "name": "sql", "extensions": [ ".sql" ]}, + { "name": "swift", "extensions": [ ".swift" ]}, + { "name": "typescript", "extensions": [ ".ts" ]}, + { "name": "vbnet", "extensions": [ ".vb" ]}, + { "name": "vba", "extensions": [ ".vba" ]}, + { "name": "vbscript", "extensions": [ ".vbs" ]}, + { "name": "vim", "extensions": [ ".vim" ]}, + { "name": "yml", "extensions": [ ".yml" ]} +]; + +export default languages;
\ No newline at end of file diff --git a/src/frontend/views/Home.vue b/src/frontend/views/Home.vue new file mode 100644 index 0000000..79ec4ab --- /dev/null +++ b/src/frontend/views/Home.vue @@ -0,0 +1,62 @@ +<template> + <HomeHeader /> + <div class="row mx-0 mt-5"> + <div id="projects-header" class="col ms-4"> + <p class="fs-1"> + Projects + </p> + </div> + <div id="projects-search" class="col d-flex justify-content-end"> + <form> + <input type="search" name="q"> + <input type="submit" value="Search"> + </form> + </div> + </div> + <div class="row mx-0"> + <div class="col ms-4"> + <ul id="repos"> + <li v-for="(project, project_name, index) in projects" :key="index"> + <div v-if="(search !== null && project_name.includes(search)) || search == null"> + <p class="fs-3"> + <router-link :to="project_name"> + {{ project_name }} + </router-link> + </p> + <span class="repo-last-updated fs-5">Last updated about {{ project["last_updated"] }} ago</span> + <span class="fs-5">{{ project["description"] }}</span> + </div> + </li> + </ul> + </div> + </div> +</template> + +<script> +import HomeHeader from "../components/HomeHeader"; +import { watch, reactive, toRefs } from "vue"; + +export default { + name: "Home", + components: { + HomeHeader + }, + setup() + { + const state = reactive({ projects: Object, search: String }); + + watch(() => + { + fetch(`http://localhost:1337/api/v1/repos`) + .then((res) => res.json()) + .then((data) => state.projects = data["data"]); + }); + + state.search = (new URLSearchParams(window.location.search)).get("q"); + + return { + ... toRefs(state) + }; + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/views/Repository.vue b/src/frontend/views/Repository.vue new file mode 100644 index 0000000..8863529 --- /dev/null +++ b/src/frontend/views/Repository.vue @@ -0,0 +1,29 @@ +<template> + <RepositoryHeader :repository="repository" /> + <router-view :repository="repository" /> +</template> + +<script> +import RepositoryHeader from "../components/RepositoryHeader"; + +export default { + name: "Repository", + components: { + RepositoryHeader + }, + props: { + repository: { + type: String, + required: true + } + }, + created() + { + if(/^\/[a-zA-Z0-9.\-_]+[/]?$/.test(window.location.pathname)) { + this.$router.push({ path: `${window.location.pathname}${( window.location.pathname.endsWith("/") ) ? "log" : "/log"}`, replace: true}); + } + + console.log(this.repository); + } +} +</script>
\ No newline at end of file diff --git a/src/frontend/views/RepositoryCommit.vue b/src/frontend/views/RepositoryCommit.vue new file mode 100644 index 0000000..283ed69 --- /dev/null +++ b/src/frontend/views/RepositoryCommit.vue @@ -0,0 +1,98 @@ +<template> + <RepositoryNavbar active-page="log" /> + <div class="row mx-0"> + <div class="col ms-2 ps-4 ps-sm-5 fs-5 vld-parent"> + <nav aria-label="breadcrumb"> + <ol class="breadcrumb"> + <li class="breadcrumb-item"> + <router-link :to="'/' + repository + '/log'"> + Log + </router-link> + </li> + <li class="breadcrumb-item active" aria-current="page"> + {{ commit }} + </li> + </ol> + </nav> + <Loading + v-model:active="is_loading" :height="24" + :width="24" color="#ffffff" + :opacity="0" /> + <table id="commit-info" class="table table-dark"> + <tbody> + <tr> + <td class="commit-info-title"> + Author + </td> + <td>{{ commit_data["author"] }}</td> + </tr> + <tr> + <td class="commit-info-title"> + Date + </td> + <td>{{ commit_data["date"] }}</td> + </tr> + <tr> + <td class="commit-info-title"> + Message + </td> + <td>{{ commit_data["message"] }}</td> + </tr> + </tbody> + </table> + + <template + v-for="(patch, index) in commit_data['patches']" :key="index"> + <CommitPatch :patch="patch" /> + </template> + </div> + </div> +</template> + +<script> +import RepositoryNavbar from "../components/RepositoryNavbar"; +import CommitPatch from "../components/CommitPatch"; +import Loading from "vue-loading-overlay"; +import 'vue-loading-overlay/dist/vue-loading.css'; +import { watch, reactive, toRefs } from "vue"; +import { format } from "date-fns"; + +export default { + name: "RepositoryCommit", + components: { + RepositoryNavbar, + CommitPatch, + Loading + }, + props: { + repository: { + type: String, + required: true + }, + commit: { + type: String, + required: true + } + }, + setup(props) + { + const state = reactive({ commit_data: {}, is_loading: true }); + + watch(() => + { + fetch(`http://localhost:1337/api/v1/repos/${props.repository}/log/${props.commit}`) + .then((res) => res.json()) + .then((data) => + { + data["data"]["date"] = format(new Date(data["data"]["date"]), "yyyy-MM-dd hh:mm"); + state.commit_data = data["data"] + state.is_loading = false; + }); + }); + + return { + ... toRefs(state) + }; + } +} +</script> diff --git a/src/frontend/views/RepositoryLog.vue b/src/frontend/views/RepositoryLog.vue new file mode 100644 index 0000000..399fc78 --- /dev/null +++ b/src/frontend/views/RepositoryLog.vue @@ -0,0 +1,91 @@ +<template> + <RepositoryNavbar active-page="log" /> + <div class="row mx-0 vld-parent"> + <Loading + v-model:active="is_loading" :height="24" + :width="24" color="#ffffff" + :opacity="0" /> + <div class="col ms-4 ps-4 ps-sm-5 mt-3"> + <table id="log" class="table table-dark fs-5"> + <thead> + <tr> + <th class="text-secondary"> + Subject + </th> + <th class="text-secondary"> + Author + </th> + <th class="text-secondary"> + Date + </th> + <th class="text-secondary"> + Files + </th> + <th class="text-secondary"> + Del/Add + </th> + </tr> + </thead> + <tbody> + <tr v-for="(commit, index) in commits" :key="index"> + <td> + <router-link :to="'log/' + commit['commit']"> + {{ commit["message"] }} + </router-link> + </td> + <td>{{ commit["author_name"] }}</td> + <td>{{ format(new Date(commit["date"]), "yyyy-MM-dd hh:mm") }}</td> + <td>{{ commit["files_changed"] }}</td> + <td><span class="text-danger">-{{ commit["deletions"] }}</span> / <span class="text-success">+{{ commit["insertions"] }}</span></td> + </tr> + </tbody> + </table> + </div> + </div> +</template> + +<script> +import RepositoryNavbar from "../components/RepositoryNavbar"; +import Loading from "vue-loading-overlay"; +import 'vue-loading-overlay/dist/vue-loading.css'; +import { format } from "date-fns"; +import { watch, reactive, toRefs } from "vue"; + +export default { + name: "RepositoryLog", + components: { + RepositoryNavbar, + Loading + }, + props: { + repository: { + type: String, + required: true + } + }, + data() + { + return { + format: format + }; + }, + setup(props) + { + const state = reactive({ commits: {}, is_loading: true }); + + watch(() => + { + fetch(`http://localhost:1337/api/v1/repos/${props.repository}/log`) + .then((res) => res.json()) + .then((data) => { + state.commits = data["data"]; + state.is_loading = false; + }); + }); + + return { + ... toRefs(state) + }; + } +} +</script>
\ No newline at end of file |