-
Notifications
You must be signed in to change notification settings - Fork 285
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 #4169 from systeminit/jobelenus/install-modules
Show modules to install, and let people install them!
- Loading branch information
Showing
10 changed files
with
336 additions
and
24 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
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,163 @@ | ||
<template> | ||
<ScrollArea | ||
class="flex flex-col h-full border border-t-0 border-neutral-300 dark:border-neutral-600" | ||
> | ||
<template #top> | ||
<div class="p-xs"> | ||
<TruncateWithTooltip | ||
class="text-2xl font-bold pb-2xs flex flex-row items-center gap-xs" | ||
> | ||
<Icon name="component" size="xl" /> | ||
<div class="flex flex-row items-center gap-xs"> | ||
{{ $props.moduleName }} | ||
</div> | ||
</TruncateWithTooltip> | ||
<VButton | ||
icon="component-plus" | ||
label="Install Module" | ||
:loading="installReqStatus.isPending" | ||
loadingText="Installing..." | ||
:requestStatus="installReqStatus" | ||
successText="Successfully Installed" | ||
tone="action" | ||
@click="install" | ||
/> | ||
</div> | ||
<div | ||
v-if="moduleObj" | ||
class="text-xs italic flex flex-row flex-wrap gap-x-lg text-neutral-600 dark:text-neutral-200" | ||
> | ||
<div> | ||
<span class="font-bold">Name: </span> | ||
{{ moduleObj.name }} | ||
</div> | ||
<div> | ||
<span class="font-bold">Module Created At: </span> | ||
<Timestamp :date="moduleObj.createdAt" size="long" /> | ||
</div> | ||
<div> | ||
<span class="font-bold">Created By: </span>{{ moduleObj.createdBy }} | ||
</div> | ||
<div> | ||
<span class="font-bold">Version: </span>{{ moduleObj.version }} | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<div | ||
v-if="loadModuleReqStatus.isError || !loadModuleReqStatus.isRequested" | ||
class="p-2 text-center text-neutral-400 dark:text-neutral-300" | ||
> | ||
<template v-if="moduleId"> | ||
Cannot retrieve details for "{{ moduleId }}" | ||
</template> | ||
</div> | ||
<template v-else-if="moduleObj"> | ||
<CodeEditor | ||
:id="`module-asset-${moduleId}`" | ||
v-model="assetFn.code" | ||
disabled | ||
:recordId="moduleId" | ||
/> | ||
<template v-for="func in functions" :key="func.id"> | ||
<h2 class="text-xl">{{ func.name }}</h2> | ||
<CodeEditor | ||
:id="`module-${func.name}-${moduleId}`" | ||
v-model="func.code" | ||
disabled | ||
:recordId="moduleId" | ||
/> | ||
</template> | ||
</template> | ||
<RequestStatusMessage | ||
v-else | ||
:requestStatus="loadModuleReqStatus" | ||
loadingMessage="Retrieving Module" | ||
/> | ||
</ScrollArea> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import { watch, computed } from "vue"; | ||
import * as _ from "lodash-es"; | ||
import { | ||
RequestStatusMessage, | ||
ScrollArea, | ||
Timestamp, | ||
VButton, | ||
Icon, | ||
} from "@si/vue-lib/design-system"; | ||
import { useModuleStore, ModuleSpec } from "@/store/module.store"; | ||
import { ModuleId } from "@/api/sdf/dal/schema"; | ||
import { nilId } from "@/utils/nilId"; | ||
import TruncateWithTooltip from "./TruncateWithTooltip.vue"; | ||
import CodeEditor from "./CodeEditor.vue"; | ||
const props = defineProps<{ | ||
moduleId: ModuleId; | ||
moduleName: string; | ||
}>(); | ||
const moduleStore = useModuleStore(); | ||
const loadModuleReqStatus = moduleStore.getRequestStatus( | ||
"GET_REMOTE_MODULE_SPEC", | ||
props.moduleId, | ||
); | ||
const installReqStatus = moduleStore.getRequestStatus( | ||
"INSTALL_REMOTE_MODULE", | ||
props.moduleId, | ||
); | ||
const moduleObj = computed<ModuleSpec | undefined>( | ||
() => moduleStore.remoteModuleSpecsById[props.moduleId], | ||
); | ||
interface FuncDisplay { | ||
id: string; | ||
name: string; | ||
code: string; | ||
} | ||
const defaultAssetFn = { | ||
id: nilId(), | ||
name: "", | ||
code: "", | ||
}; | ||
const assetFn = computed<FuncDisplay>(() => { | ||
if (!moduleObj.value) return defaultAssetFn; | ||
const f = moduleObj.value.funcs | ||
.filter((f) => !f.name.startsWith("si:")) | ||
.find((f) => f.data.backendKind === "jsSchemaVariantDefinition"); | ||
if (!f) { | ||
return defaultAssetFn; | ||
} else { | ||
return { | ||
id: f.uniqueId, | ||
name: f.name, | ||
code: Buffer.from(f.data.codeBase64 || "", "base64").toString(), | ||
}; | ||
} | ||
}); | ||
const functions = computed<FuncDisplay[]>(() => { | ||
if (!moduleObj.value) return []; | ||
return moduleObj.value.funcs | ||
.filter((f) => !f.name.startsWith("si:")) | ||
.filter((f) => f.data.backendKind !== "jsSchemaVariantDefinition") | ||
.map((f) => { | ||
const code = Buffer.from(f.data.codeBase64 || "", "base64").toString(); | ||
const display = {} as FuncDisplay; | ||
display.id = f.uniqueId; | ||
display.name = f.name; | ||
display.code = code; | ||
return display; | ||
}); | ||
}); | ||
const install = () => moduleStore.INSTALL_REMOTE_MODULE(props.moduleId); | ||
watch( | ||
() => props.moduleId, | ||
() => { | ||
moduleStore.GET_REMOTE_MODULE_SPEC(props.moduleId); | ||
}, | ||
{ immediate: true }, | ||
); | ||
</script> |
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,104 @@ | ||
<template> | ||
<ScrollArea> | ||
<RequestStatusMessage | ||
v-if="loadModulesReqStatus.isPending" | ||
:requestStatus="loadModulesReqStatus" | ||
loadingMessage="Loading modules..." | ||
/> | ||
<template #top> | ||
<SidebarSubpanelTitle icon="component"> | ||
<template #label> | ||
<div class="flex flex-row gap-xs"> | ||
<div>Install New Assets</div> | ||
</div> | ||
</template> | ||
</SidebarSubpanelTitle> | ||
<SiSearch | ||
ref="searchRef" | ||
placeholder="search assets" | ||
@search="onSearch" | ||
/> | ||
</template> | ||
<ul | ||
v-if="assetStore.installableModules.length > 0" | ||
:class=" | ||
clsx( | ||
'dark:text-white text-black dark:bg-neutral-800 py-[1px]', | ||
'hover:dark:outline-action-300 hover:outline-action-500 hover:outline hover:z-10 hover:-outline-offset-1 hover:outline-1', | ||
) | ||
" | ||
> | ||
<li | ||
v-for="module in filteredModules" | ||
:key="module.id" | ||
:class=" | ||
clsx( | ||
'text-xs w-full p-2xs truncate flex flex-row items-center gap-1 hover:text-action-500 dark:hover:text-action-300 cursor-pointer', | ||
selectedModule && | ||
module.id === selectedModule.id && | ||
themeClasses('bg-action-100', 'bg-action-900'), | ||
) | ||
" | ||
@click="() => selectModule(module)" | ||
> | ||
<div class="truncate"> | ||
{{ module.name }} | ||
</div> | ||
</li> | ||
</ul> | ||
<EmptyStateCard | ||
v-else | ||
iconName="no-assets" | ||
primaryText="No Installable Assets" | ||
secondaryText="Check back later when more assets are contributed." | ||
/> | ||
</ScrollArea> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import * as _ from "lodash-es"; | ||
import clsx from "clsx"; | ||
import { ref, computed } from "vue"; | ||
import { | ||
ScrollArea, | ||
RequestStatusMessage, | ||
themeClasses, | ||
} from "@si/vue-lib/design-system"; | ||
import SiSearch from "@/components/SiSearch.vue"; | ||
import { useAssetStore } from "@/store/asset.store"; | ||
import { Module } from "@/api/sdf/dal/schema"; | ||
import router from "@/router"; | ||
import EmptyStateCard from "./EmptyStateCard.vue"; | ||
import SidebarSubpanelTitle from "./SidebarSubpanelTitle.vue"; | ||
const assetStore = useAssetStore(); | ||
const searchRef = ref<InstanceType<typeof SiSearch>>(); | ||
const searchString = ref(""); | ||
const onSearch = (search: string) => { | ||
searchString.value = search.trim().toLocaleLowerCase(); | ||
}; | ||
const filteredModules = computed(() => | ||
assetStore.installableModules.filter((m) => | ||
m.name.toLocaleLowerCase().includes(searchString.value), | ||
), | ||
); | ||
const loadModulesReqStatus = assetStore.getRequestStatus("LOAD_MODULES"); | ||
const selectedModule = ref<Module | undefined>(); | ||
const selectModule = (module: Module) => { | ||
selectedModule.value = module; | ||
const newQueryObj = { | ||
...{ m: module.id }, | ||
}; | ||
router.replace({ | ||
query: newQueryObj, | ||
}); | ||
}; | ||
defineExpose({ selectedModule }); | ||
</script> |
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
Oops, something went wrong.