mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into adminCreateMultiplePendingCreations
This commit is contained in:
commit
0a57d882c6
31
.github/workflows/test.yml
vendored
31
.github/workflows/test.yml
vendored
@ -47,7 +47,7 @@ jobs:
|
||||
##########################################################################
|
||||
- name: Admin | Build `test` image
|
||||
run: |
|
||||
docker build --target test -t "gradido/admin:test" admin/
|
||||
docker build --target test -t "gradido/admin:test" admin/ --build-arg NODE_ENV="test"
|
||||
docker save "gradido/admin:test" > /tmp/admin.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -294,6 +294,35 @@ jobs:
|
||||
- name: Admin Interface | Lint
|
||||
run: docker run --rm gradido/admin:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LOCALES ADMIN ######################################################
|
||||
##############################################################################
|
||||
locales_admin:
|
||||
name: Locales - Admin
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_admin]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Admin Interface)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-admin-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/admin.tar
|
||||
##########################################################################
|
||||
# LOCALES FRONTEND #######################################################
|
||||
##########################################################################
|
||||
- name: admin | Locales
|
||||
run: docker run --rm gradido/admin:test yarn run locales
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT BACKEND ##########################################################
|
||||
##############################################################################
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,5 @@
|
||||
*.log
|
||||
/node_modules/*
|
||||
.vscode
|
||||
messages.pot
|
||||
nbproject
|
||||
.metadata
|
||||
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -2,6 +2,7 @@
|
||||
"recommendations": [
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
"esbenp.prettier-vscode",
|
||||
"hediet.vscode-drawio"
|
||||
]
|
||||
}
|
||||
@ -13,7 +13,7 @@ ENV BUILD_VERSION="0.0.0.0"
|
||||
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
||||
ENV BUILD_COMMIT="0000000"
|
||||
## SET NODE_ENV
|
||||
ENV NODE_ENV="production"
|
||||
ARG NODE_ENV="production"
|
||||
## App relevant Envs
|
||||
ENV PORT="8080"
|
||||
|
||||
|
||||
@ -1,4 +1,15 @@
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env'],
|
||||
plugins: ['transform-require-context'],
|
||||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
|
||||
const presets = ['@babel/preset-env']
|
||||
const plugins = []
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
plugins.push('transform-require-context')
|
||||
}
|
||||
|
||||
return {
|
||||
presets,
|
||||
plugins,
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
"dev": "yarn run serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"test": "jest --coverage"
|
||||
"test": "jest --coverage",
|
||||
"locales": "scripts/missing-keys.sh && scripts/sort.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
|
||||
17
admin/scripts/missing-keys.sh
Executable file
17
admin/scripts/missing-keys.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
ROOT_DIR=$(dirname "$0")/..
|
||||
|
||||
sorting="jq -f $ROOT_DIR/scripts/sort_filter.jq"
|
||||
english="$sorting $ROOT_DIR/src/locales/en.json"
|
||||
german="$sorting $ROOT_DIR/src/locales/de.json"
|
||||
listPaths="jq -c 'path(..)|[.[]|tostring]|join(\".\")'"
|
||||
diffString="<( $english | $listPaths ) <( $german | $listPaths )"
|
||||
if eval "diff -q $diffString";
|
||||
then
|
||||
: # all good
|
||||
else
|
||||
eval "diff -y $diffString | grep '[|<>]'";
|
||||
printf "\nEnglish and German translation keys do not match, see diff above.\n"
|
||||
exit 1
|
||||
fi
|
||||
25
admin/scripts/sort.sh
Executable file
25
admin/scripts/sort.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
ROOT_DIR=$(dirname "$0")/..
|
||||
|
||||
tmp=$(mktemp)
|
||||
exit_code=0
|
||||
|
||||
for locale_file in $ROOT_DIR/src/locales/*.json
|
||||
do
|
||||
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
|
||||
if [ "$*" == "--fix" ]
|
||||
then
|
||||
mv "$tmp" $locale_file
|
||||
else
|
||||
if diff -q "$tmp" $locale_file > /dev/null ;
|
||||
then
|
||||
: # all good
|
||||
else
|
||||
exit_code=$?
|
||||
echo "$(basename -- $locale_file) is not sorted by keys"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit $exit_code
|
||||
13
admin/scripts/sort_filter.jq
Normal file
13
admin/scripts/sort_filter.jq
Normal file
@ -0,0 +1,13 @@
|
||||
def walk(f):
|
||||
. as $in
|
||||
| if type == "object" then
|
||||
reduce keys_unsorted[] as $key
|
||||
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||
elif type == "array" then map( walk(f) ) | f
|
||||
else f
|
||||
end;
|
||||
|
||||
def keys_sort_by(f):
|
||||
to_entries | sort_by(.key|f ) | from_entries;
|
||||
|
||||
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)
|
||||
@ -172,14 +172,17 @@ export default {
|
||||
currentMonth: {
|
||||
short: this.$moment().format('MMMM'),
|
||||
long: this.$moment().format('YYYY-MM-DD'),
|
||||
year: this.$moment().format('YYYY'),
|
||||
},
|
||||
lastMonth: {
|
||||
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
||||
long: this.$moment().subtract(1, 'month').format('YYYY-MM') + '-01',
|
||||
year: this.$moment().subtract(1, 'month').format('YYYY'),
|
||||
},
|
||||
beforeLastMonth: {
|
||||
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
||||
long: this.$moment().subtract(2, 'month').format('YYYY-MM') + '-01',
|
||||
year: this.$moment().subtract(2, 'month').format('YYYY'),
|
||||
},
|
||||
submitObj: null,
|
||||
isdisabled: true,
|
||||
@ -191,6 +194,7 @@ export default {
|
||||
// Auswählen eines Zeitraumes
|
||||
updateRadioSelected(name, index, openCreation) {
|
||||
this.createdIndex = index
|
||||
this.text = 'Schöpfung für ' + name.short + ' ' + name.year
|
||||
// Wenn Mehrfachschöpfung
|
||||
if (this.type === 'massCreation') {
|
||||
// An Creation.vue emitten und radioSelectedMass aktualisieren
|
||||
|
||||
@ -36,6 +36,10 @@
|
||||
hover
|
||||
stacked="md"
|
||||
>
|
||||
<template #cell(creation)="data">
|
||||
<div v-html="data.value"></div>
|
||||
</template>
|
||||
|
||||
<template #cell(edit_creation)="row">
|
||||
<b-button variant="info" size="md" @click="rowToogleDetails(row, 0)" class="mr-2">
|
||||
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
||||
|
||||
@ -4,7 +4,7 @@ import VueI18n from 'vue-i18n'
|
||||
Vue.use(VueI18n)
|
||||
|
||||
const loadLocaleMessages = () => {
|
||||
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
|
||||
const locales = require.context('./locales/', true, /[A-Za-z0-9-_,\s]+\.json$/i)
|
||||
const messages = {}
|
||||
locales.keys().forEach((key) => {
|
||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
|
||||
|
||||
@ -1 +1,4 @@
|
||||
{}
|
||||
{
|
||||
"not_open_creations": "keine offene Schöpfungen",
|
||||
"open_creations": "offene Schöpfungen"
|
||||
}
|
||||
|
||||
@ -1 +1,4 @@
|
||||
{}
|
||||
{
|
||||
"not_open_creations": "No open creations",
|
||||
"open_creations": "Open creations"
|
||||
}
|
||||
|
||||
16
admin/src/locales/index.js
Normal file
16
admin/src/locales/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
const locales = [
|
||||
{
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
iso: 'en-US',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: 'Deutsch',
|
||||
code: 'de',
|
||||
iso: 'de-DE',
|
||||
enabled: true,
|
||||
},
|
||||
]
|
||||
|
||||
export default locales
|
||||
@ -31,6 +31,7 @@ const mocks = {
|
||||
openCreations: 2,
|
||||
},
|
||||
},
|
||||
$t: jest.fn((t) => t),
|
||||
}
|
||||
|
||||
describe('Overview', () => {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<b-card
|
||||
v-show="$store.state.openCreations > 0"
|
||||
border-variant="primary"
|
||||
header="offene Schöpfungen"
|
||||
:header="$t('open_creations')"
|
||||
header-bg-variant="danger"
|
||||
header-text-variant="white"
|
||||
align="center"
|
||||
@ -17,7 +17,7 @@
|
||||
<b-card
|
||||
v-show="$store.state.openCreations < 1"
|
||||
border-variant="success"
|
||||
header="keine offene Schöpfungen"
|
||||
:header="$t('not_open_creations')"
|
||||
header-bg-variant="success"
|
||||
header-text-variant="white"
|
||||
align="center"
|
||||
|
||||
@ -26,6 +26,16 @@ const mocks = {
|
||||
$toasted: {
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$moment: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
subtract: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
describe('UserSearch', () => {
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div class="user-search">
|
||||
<div style="text-align: right">
|
||||
<b-button block variant="danger" @click="unconfirmedRegisterMails">
|
||||
<b-icon icon="envelope" variant="light"></b-icon>
|
||||
Anzeigen aller nicht registrierten E-Mails.
|
||||
</b-button>
|
||||
</div>
|
||||
<label>Usersuche</label>
|
||||
<b-input
|
||||
type="text"
|
||||
@ -15,12 +21,7 @@
|
||||
:fieldsTable="fields"
|
||||
:criteria="criteria"
|
||||
/>
|
||||
<div>
|
||||
<b-button block variant="danger" @click="unconfirmedRegisterMails">
|
||||
<b-icon icon="envelope" variant="light"></b-icon>
|
||||
Anzeigen aller nicht registrierten E-Mails.
|
||||
</b-button>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -41,9 +42,27 @@ export default {
|
||||
{ key: 'lastName', label: 'Lastname' },
|
||||
{
|
||||
key: 'creation',
|
||||
label: 'Creation',
|
||||
label: 'Open creations',
|
||||
formatter: (value, key, item) => {
|
||||
return String(value)
|
||||
return (
|
||||
`
|
||||
<div>` +
|
||||
this.$moment().subtract(2, 'month').format('MMMM') +
|
||||
` - ` +
|
||||
String(value[0]) +
|
||||
` GDD</div>
|
||||
<div>` +
|
||||
this.$moment().subtract(1, 'month').format('MMMM') +
|
||||
` - ` +
|
||||
String(value[1]) +
|
||||
` GDD</div>
|
||||
<div>` +
|
||||
this.$moment().format('MMMM') +
|
||||
` - ` +
|
||||
String(value[2]) +
|
||||
` GDD</div>
|
||||
`
|
||||
)
|
||||
},
|
||||
},
|
||||
{ key: 'show_details', label: 'Details' },
|
||||
@ -53,6 +72,15 @@ export default {
|
||||
searchResult: [],
|
||||
massCreation: [],
|
||||
criteria: '',
|
||||
currentMonth: {
|
||||
short: this.$moment().format('MMMM'),
|
||||
},
|
||||
lastMonth: {
|
||||
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
||||
},
|
||||
beforeLastMonth: {
|
||||
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
100
docu/graphics/roadmap.drawio
Normal file
100
docu/graphics/roadmap.drawio
Normal file
@ -0,0 +1,100 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="5Rbgv0eSL3tDJ_YiGKbA" name="Page-1">
|
||||
<mxGraphModel dx="1406" dy="633" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="Release 1.6.X" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="40" width="560" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="Development<br style="font-size: 12px">15.10.2021 -<br style="font-size: 12px">30.12.2021&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="120" width="400" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="4" value="Testing<br style="font-size: 7px;">31.12.2021 -<br style="font-size: 7px;">07.01.2022&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=7;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="120" width="40" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="5" value="Release Window<br style="font-size: 7px;">08.01.2022 -<br style="font-size: 7px;">15.01.2022&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=7;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="120" width="40" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="Deadline<br style="font-size: 12px;">31.01.2022&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="520" y="120" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="Release 1.7.X" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="40" width="560" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" value="Admin Interface" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="200" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="Replace Login Server" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="240" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="Replace Community Server" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="280" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="Refine/Partially redesign Register Process" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="320" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="PublisherID Field on Register" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="360" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="GDD Decay Calculator Tool" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="400" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="14" value="Refactors, Tests &amp; Devops (e.g. Seeding)" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="560" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="15" value="Seperate Overview from SendCoin" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="440" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="16" value="Minor Design &amp; Layout changes in Frontend" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="480" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" value="" style="rounded=0;whiteSpace=wrap;html=1;fontSize=7;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="200" width="40" height="400" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" value="" style="rounded=0;whiteSpace=wrap;html=1;fontSize=7;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="200" width="40" height="400" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="19" value="New Deployment" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="520" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="22" value="Start refactoring" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="520" y="200" width="80" height="400" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="23" value="Development<br style="font-size: 12px">01.02.2022 -<br style="font-size: 12px">28.02.2022&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="120" width="400" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="24" value="Testing<br style="font-size: 12px">01.03.2022 -<br style="font-size: 12px">07.03.2022&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="1000" y="120" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="25" value="Release Window<br style="font-size: 12px">08.03.2022 -<br style="font-size: 12px">15.03.2022&nbsp;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="1080" y="120" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="26" value="Refactoring" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="200" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="28" value="" style="rounded=0;whiteSpace=wrap;html=1;fontSize=7;" parent="1" vertex="1">
|
||||
<mxGeometry x="1000" y="200" width="80" height="240" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="" style="rounded=0;whiteSpace=wrap;html=1;fontSize=7;" parent="1" vertex="1">
|
||||
<mxGeometry x="1080" y="200" width="80" height="240" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="30" value="Admin Interface User Tools" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="240" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="31" value="Make Klicktipp Community Ready" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="280" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="Make GDT Community Ready" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="320" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="Implement a Federation Solution &amp; Experiment" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="360" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="Frontend Community/Account statistics" style="rounded=0;whiteSpace=wrap;html=1;fontSize=12;" parent="1" vertex="1">
|
||||
<mxGeometry x="600" y="400" width="400" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
docu/graphics/roadmap.png
Normal file
BIN
docu/graphics/roadmap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Loading…
x
Reference in New Issue
Block a user