-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #97 from ssciwr/admin_interface_milestones
Add milestones to admin interface
- Loading branch information
Showing
12 changed files
with
444 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -170,3 +170,6 @@ dmypy.json | |
|
||
# static images | ||
mondey_backend/static/*.jpg | ||
|
||
# ssl keys | ||
*.pem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,12 @@ sudo docker compose ps | |
sudo docker compose logs | ||
``` | ||
|
||
To update the running website to the latest version: | ||
|
||
``` | ||
sudo docker compose pull && sudo docker compose up -d && sudo docker system prune -af | ||
``` | ||
|
||
### Give users admin rights | ||
|
||
To make an existing user with email `[email protected]` into an admin, modify the users database, e.g. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# Development | ||
|
||
Some information on how to locally build and serve the website if you would like to make changes to the code. | ||
There are two ways to do this: | ||
|
||
- docker | ||
- closer to production environment | ||
- but less convenient for development - you need to rebuild the image every time you make a change | ||
- python/pnpm | ||
- further from production environment setup | ||
- but convenient for development - see changes immediately without having to rebuild or restart anything | ||
|
||
## Run locally with docker | ||
|
||
Requires docker and docker compose. | ||
|
||
1. clone the repo: | ||
|
||
```sh | ||
git clone https://github.com/ssciwr/mondey.git | ||
cd mondey | ||
``` | ||
|
||
2. generate a local SSL cert/key pair: | ||
|
||
``` | ||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=localhost' | ||
``` | ||
|
||
3. build and run the website locally in docker containers on your computer: | ||
|
||
```sh | ||
docker compose up --build -d | ||
``` | ||
|
||
The website is then served at https://localhost/ | ||
(note that the SSL keys are self-signed keys and your browser will still warn about the site being insecure.) | ||
|
||
Whenever you make a change to the code you need to re-run the above command to see the effect of your changes. | ||
|
||
### Database | ||
|
||
The databases will by default be stored in a `db` folder | ||
in the folder where you run the docker compose command. | ||
|
||
### Make yourself an admin user | ||
|
||
``` | ||
sudo sqlite3 docker_volume/predicTCR.db | ||
sqlite> UPDATE user SET is_admin=true WHERE email='[email protected]'; | ||
sqlite> .quit | ||
``` | ||
|
||
## Run locally with Python and pnpm | ||
|
||
Requires Python and [pnpm](https://pnpm.io/installation#using-a-standalone-script) | ||
|
||
1. clone the repo: | ||
|
||
```sh | ||
git clone https://github.com/ssciwr/mondey.git | ||
cd mondey | ||
``` | ||
|
||
2. install and run the backend development server: | ||
|
||
```sh | ||
cd mondey_backend | ||
pip install . | ||
cd .. | ||
mondey-backend | ||
``` | ||
|
||
3. install and run the frontend development server: | ||
|
||
```sh | ||
cd frontend | ||
pnpm install | ||
pnpm run dev | ||
``` | ||
|
||
The website is then served at http://localhost:5173/. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
frontend/src/lib/components/Admin/EditMilestoneModal.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<script lang="ts"> | ||
import { | ||
Button, | ||
InputAddon, | ||
Textarea, | ||
Input, | ||
Label, | ||
ButtonGroup, | ||
Fileupload, | ||
Modal | ||
} from 'flowbite-svelte'; | ||
import { languages } from '$lib/stores/adminStore'; | ||
import { refreshMilestoneGroups, updateMilestone, uploadMilestoneImage } from '$lib/admin'; | ||
export let open: boolean = false; | ||
export let milestone: object | null = null; | ||
let files: FileList; | ||
let images: string[] = []; | ||
async function uploadImagesChanged(event) { | ||
const uploaded_files = [...event.target.files]; | ||
images = await Promise.all( | ||
uploaded_files.map((f) => { | ||
return imageAsDataURL(f); | ||
}) | ||
); | ||
} | ||
function imageAsDataURL(file: Blob) { | ||
return new Promise((resolve, reject) => { | ||
const fileReader = new FileReader(); | ||
fileReader.onload = function () { | ||
return resolve(fileReader.result); | ||
}; | ||
fileReader.onerror = function () { | ||
fileReader.abort(); | ||
return reject(fileReader.error); | ||
}; | ||
fileReader.readAsDataURL(file); | ||
}); | ||
} | ||
export async function saveChanges() { | ||
try { | ||
await updateMilestone(milestone); | ||
if (files) { | ||
for (const file of files) { | ||
await uploadMilestoneImage(milestone.id, file); | ||
} | ||
} | ||
await refreshMilestoneGroups(); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
</script> | ||
|
||
<Modal title="Edit milestone" bind:open autoclose size="xl"> | ||
{#if milestone} | ||
<div class="mb-5"> | ||
<Label class="mb-2">Title</Label> | ||
{#each Object.entries(milestone.text) as [lang_id, text]} | ||
<div class="mb-1"> | ||
<ButtonGroup class="w-full"> | ||
<InputAddon>{$languages[text.lang_id]}</InputAddon> | ||
<Input bind:value={text.title} placeholder="Title" /> | ||
</ButtonGroup> | ||
</div> | ||
{/each} | ||
</div> | ||
<div class="mb-5"> | ||
<Label class="mb-2">Description</Label> | ||
{#each Object.entries(milestone.text) as [lang_id, text]} | ||
<div class="mb-1"> | ||
<ButtonGroup class="w-full"> | ||
<InputAddon>{$languages[text.lang_id]}</InputAddon> | ||
<Textarea bind:value={text.desc} placeholder="Description" /> | ||
</ButtonGroup> | ||
</div> | ||
{/each} | ||
</div> | ||
<div class="mb-5"> | ||
<Label class="mb-2">Observation</Label> | ||
{#each Object.entries(milestone.text) as [lang_id, text]} | ||
<div class="mb-1"> | ||
<ButtonGroup class="w-full"> | ||
<InputAddon>{$languages[text.lang_id]}</InputAddon> | ||
<Textarea bind:value={text.obs} placeholder="Observation" /> | ||
</ButtonGroup> | ||
</div> | ||
{/each} | ||
</div> | ||
<div class="mb-5"> | ||
<Label class="mb-2">Help</Label> | ||
{#each Object.entries(milestone.text) as [lang_id, text]} | ||
<div class="mb-1"> | ||
<ButtonGroup class="w-full"> | ||
<InputAddon>{$languages[text.lang_id]}</InputAddon> | ||
<Textarea bind:value={text.help} placeholder="Help" /> | ||
</ButtonGroup> | ||
</div> | ||
{/each} | ||
</div> | ||
<div class="mb-5"> | ||
<Label for="img_upload" class="pb-2">Images</Label> | ||
<div class="flex flex-row"> | ||
{#each milestone.images as milestoneImage, milestoneImageId (milestoneImage.id)} | ||
<img | ||
src={`${import.meta.env.VITE_MONDEY_API_URL}/static/${milestoneImage.filename}`} | ||
width="48" | ||
height="48" | ||
alt={`${milestoneImageId}`} | ||
class="m-2" | ||
/> | ||
{/each} | ||
{#each images as image} | ||
<img src={image} width="48" height="48" alt="milestone" class="m-2" /> | ||
{/each} | ||
</div> | ||
<Fileupload | ||
bind:files | ||
on:change={uploadImagesChanged} | ||
multiple | ||
accept=".jpg, .jpeg" | ||
id="img_upload" | ||
class="mb-2 flex-grow-0" | ||
/> | ||
</div> | ||
{/if} | ||
<svelte:fragment slot="footer"> | ||
<Button color="green" on:click={saveChanges}>Save changes</Button> | ||
<Button color="alternative">Cancel</Button> | ||
</svelte:fragment> | ||
</Modal> |
Oops, something went wrong.