Ver Fonte

image selector frontend cleanup

jusrhee há 5 anos atrás
pai
commit
3e6b552e8d

+ 4 - 0
dashboard/src/assets/edit.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path opacity="0.4" d="M16.6643 21.9897H7.33488C5.88835 22.0796 4.46781 21.5781 3.3989 20.6011C2.4219 19.5312 1.92041 18.1107 2.01032 16.6652V7.33482C1.92041 5.88932 2.4209 4.46878 3.3979 3.39889C4.46781 2.42189 5.88835 1.92041 7.33488 2.01032H16.6643C18.1089 1.92041 19.5284 2.4209 20.5973 3.39789C21.5733 4.46878 22.0758 5.88832 21.9899 7.33482V16.6652C22.0788 18.1107 21.5783 19.5312 20.6013 20.6011C19.5314 21.5781 18.1109 22.0796 16.6643 21.9897Z" fill="white"/>
+<path d="M17.0545 10.3976L10.5018 16.9829C10.161 17.3146 9.7131 17.5 9.24574 17.5H6.95762C6.83105 17.5 6.71421 17.4512 6.62658 17.3634C6.53895 17.2756 6.5 17.1585 6.5 17.0317L6.55842 14.7195C6.56816 14.261 6.75315 13.8317 7.07446 13.5098L11.7189 8.8561C11.7967 8.77805 11.9331 8.77805 12.011 8.8561L13.6399 10.4785C13.747 10.5849 13.9028 10.6541 14.0683 10.6541C14.4286 10.6541 14.7109 10.3615 14.7109 10.0102C14.7109 9.83463 14.6428 9.67854 14.5357 9.56146C14.5065 9.52244 12.9554 7.97805 12.9554 7.97805C12.858 7.88049 12.858 7.71463 12.9554 7.61707L13.6078 6.95366C14.2114 6.34878 15.1851 6.34878 15.7888 6.95366L17.0545 8.22195C17.6485 8.81707 17.6485 9.79268 17.0545 10.3976Z" fill="white"/>
+</svg>

BIN
dashboard/src/assets/tag.png


+ 38 - 15
dashboard/src/components/image-selector/ImageSelector.tsx

@@ -1,12 +1,15 @@
 import React, { Component } from 'react';
 import styled from 'styled-components';
 import info from '../../assets/info.svg';
+import edit from '../../assets/edit.svg';
 
 import api from '../../shared/api';
 import { getIntegrationIcon } from '../../shared/common';
 import { Context } from '../../shared/Context';
+import { ImageType } from '../../shared/types';
 
 import Loading from '../Loading';
+import TagList from './TagList';
 
 type PropsType = {
   forceExpanded?: boolean,
@@ -18,14 +21,14 @@ type StateType = {
   isExpanded: boolean,
   loading: boolean,
   error: boolean,
-  images: any[],
-  clickedImage: any | null,
+  images: ImageType[],
+  clickedImage: ImageType | null,
 };
 
 const dummyImages = [
   {
     kind: 'docker-hub',
-    source: 'https://index.docker.io/jusrhee/image1',
+    source: 'index.docker.io/jusrhee/image1',
   },
   {
     kind: 'docker-hub',
@@ -58,8 +61,8 @@ export default class ImageSelector extends Component<PropsType, StateType> {
     isExpanded: this.props.forceExpanded,
     loading: false,
     error: false,
-    images: [] as any[],
-    clickedImage: null as any | null,
+    images: [] as ImageType[],
+    clickedImage: null as ImageType | null,
   }
 
   componentDidMount() {
@@ -74,7 +77,7 @@ export default class ImageSelector extends Component<PropsType, StateType> {
       return <LoadingWrapper>Error loading repos</LoadingWrapper>
     }
 
-    return images.map((image: any, i: number) => {
+    return images.map((image: ImageType, i: number) => {
       let icon = getIntegrationIcon(image.kind);
       return (
         <ImageItem
@@ -111,19 +114,39 @@ export default class ImageSelector extends Component<PropsType, StateType> {
   }
 
   renderExpanded = () => {
-    return (
-      <div>
-        <ExpandedWrapper>
-          {this.renderImageList()}
-        </ExpandedWrapper>
-        {this.renderBackButton()}
-      </div>
-    );
+    let { selectedImageUrl, setSelectedImageUrl } = this.props;
+    if (!this.state.clickedImage) {
+      return (
+        <div>
+          <ExpandedWrapper>
+            {this.renderImageList()}
+          </ExpandedWrapper>
+          {this.renderBackButton()}
+        </div>
+      );
+    } else {
+      return (
+        <div>
+          <ExpandedWrapper>
+            <TagList
+              selectedImageUrl={selectedImageUrl}
+              setSelectedImageUrl={setSelectedImageUrl}
+            />
+          </ExpandedWrapper>
+          {this.renderBackButton()}
+        </div>
+      );
+    }
   }
 
   renderSelected = () => {
     let { selectedImageUrl, setSelectedImageUrl } = this.props;
     let icon = info;
+    if (this.state.clickedImage) {
+      icon = getIntegrationIcon(this.state.clickedImage.kind);
+    } else if (selectedImageUrl && selectedImageUrl !== '') {
+      icon = edit;
+    }
     return (
       <Label>
         <img src={icon} />
@@ -131,7 +154,7 @@ export default class ImageSelector extends Component<PropsType, StateType> {
           onClick={(e: any) => e.stopPropagation()}
           value={selectedImageUrl}
           onChange={(e: any) => { 
-            setSelectedImageUrl(e.value); 
+            setSelectedImageUrl(e.target.value); 
             this.setState({ clickedImage: null });
           }}
           placeholder='Enter or select your container image URL'

+ 142 - 0
dashboard/src/components/image-selector/TagList.tsx

@@ -0,0 +1,142 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import tag_icon from '../../assets/tag.png';
+import info from '../../assets/info.svg';
+
+import api from '../../shared/api';
+
+import Loading from '../Loading';
+
+type PropsType = {
+  setSelectedImageUrl: (x: string) => void,
+  selectedImageUrl: string
+};
+
+type StateType = {
+  loading: boolean,
+  error: boolean,
+  tags: string[],
+  currentTag: string | null,
+};
+
+export default class TagList extends Component<PropsType, StateType> {
+  state = {
+    loading: true,
+    error: false,
+    tags: [] as string[],
+    currentTag: null as string | null,
+  }
+
+  componentDidMount() {
+    this.setState({ tags: ['123', '456', '889', '5521', '5212'], loading: false });
+
+    /* Get branches
+    api.getTags('<token>', {}, {
+
+    }, (err: any, res: any) => {
+      if (err) {
+        this.setState({ loading: false, error: true });
+      } else {
+        this.setState({ tags: res.data, loading: false, error: false });
+      }
+    });
+    */
+  }
+
+  setTag = (tag: string) => {
+    let { selectedImageUrl, setSelectedImageUrl} = this.props;
+    let splits = selectedImageUrl.split(':');
+    if (splits[splits.length - 1] === this.state.currentTag) {
+      selectedImageUrl = splits.reduce((acc: string, curr: string) => {
+        if (curr !== this.state.currentTag) {
+          return acc + ':' + curr;
+        } else {
+          return acc;
+        }
+      });
+    }
+    setSelectedImageUrl(selectedImageUrl + ':' + tag);
+    this.setState({ currentTag: tag });
+  }
+
+  renderTagList = () => {
+    let { tags, loading, error } = this.state;
+    if (loading) {
+      return <LoadingWrapper><Loading /></LoadingWrapper>
+    } else if (error || !tags) {
+      return <LoadingWrapper>Error loading tags</LoadingWrapper>
+    }
+
+    return tags.map((tag: string, i: number) => {
+      return (
+        <TagName
+          key={i}
+          isSelected={tag === this.state.currentTag}
+          lastItem={i === tags.length - 1}
+          onClick={() => this.setTag(tag)}
+        >
+          <img src={tag_icon} />{tag}
+        </TagName>
+      );
+    });
+  }
+
+  render() {
+    return (
+      <div>
+        <TagNameAlt>
+          <img src={info} /> Select Image Tag
+        </TagNameAlt>
+        {this.renderTagList()}
+      </div>
+    );
+  }
+}
+
+const TagName = styled.div`
+  display: flex;
+  width: 100%;
+  font-size: 13px;
+  border-bottom: 1px solid ${(props: { lastItem?: boolean, isSelected?: boolean }) => props.lastItem ? '#00000000' : '#606166'};
+  color: #ffffff;
+  user-select: none;
+  align-items: center;
+  padding: 10px 0px;
+  cursor: pointer;
+  background: ${(props: { isSelected?: boolean, lastItem?: boolean }) => props.isSelected ? '#ffffff22' : '#ffffff11'};
+  :hover {
+    background: #ffffff22;
+
+    > i {
+      background: #ffffff22;
+    }
+  }
+
+  > img {
+    width: 18px;
+    height: 18px;
+    margin-left: 12px;
+    margin-right: 12px;
+  }
+`;
+
+const TagNameAlt = styled(TagName)`
+  color: #ffffff55;
+  cursor: default;
+  :hover {
+    background: #ffffff11;
+    > i {
+      background: none;
+    }
+  }
+`;
+
+const LoadingWrapper = styled.div`
+  padding: 30px 0px;
+  background: #ffffff11;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 13px;
+  color: #ffffff44;
+`;

+ 0 - 3
dashboard/src/components/repo-selector/BranchList.tsx

@@ -1,10 +1,8 @@
-import { stringify } from 'querystring';
 import React, { Component } from 'react';
 import styled from 'styled-components';
 import branch_icon from '../../assets/branch.png';
 
 import api from '../../shared/api';
-import { RepoType } from '../../shared/types';
 
 import Loading from '../Loading';
 
@@ -22,7 +20,6 @@ type StateType = {
 
 export default class BranchList extends Component<PropsType, StateType> {
   state = {
-    selectedBranch: '',
     loading: true,
     error: false,
     branches: [] as string[]

+ 34 - 2
dashboard/src/main/home/integrations/integration-form/ECRForm.tsx

@@ -14,18 +14,50 @@ type PropsType = {
 };
 
 type StateType = {
+  credentialsName: string,
+  awsAccessId: string,
+  awsSecretKey: string,
 };
 
 export default class ECRForm extends Component<PropsType, StateType> {
   state = {
+    credentialsName: '',
+    awsAccessId: '',
+    awsSecretKey: '',
   }
 
   render() {
     return ( 
       <StyledForm>
         <CredentialWrapper>
-          <Heading>Coming Soon</Heading>
-          <Helper>Under construction.</Helper>
+          <Heading>Porter Settings</Heading>
+          <Helper>Give a name to this set of registry credentials (just for Porter).</Helper>
+          <InputRow
+            type='text'
+            value={this.state.credentialsName}
+            setValue={(x: string) => this.setState({ credentialsName: x })}
+            label='🏷️ Registry Name'
+            placeholder='ex: paper-straw'
+            width='100%'
+          />
+          <Heading>AWS Settings</Heading>
+          <Helper>AWS access credentials.</Helper>
+          <InputRow
+            type='text'
+            value={this.state.awsAccessId}
+            setValue={(x: string) => this.setState({ awsAccessId: x })}
+            label='👤 AWS Access ID'
+            placeholder='ex: AKIAIOSFODNN7EXAMPLE'
+            width='100%'
+          />
+          <InputRow
+            type='password'
+            value={this.state.awsSecretKey}
+            setValue={(x: string) => this.setState({ awsSecretKey: x })}
+            label='🔒 AWS Secret Key'
+            placeholder='○ ○ ○ ○ ○ ○ ○ ○ ○'
+            width='100%'
+          />
         </CredentialWrapper>
         <SaveButton
           text='Save Settings'

+ 1 - 1
dashboard/src/main/home/integrations/integration-form/EKSForm.tsx

@@ -61,7 +61,7 @@ export default class EKSForm extends Component<PropsType, StateType> {
           />
 
           <Heading>AWS Settings</Heading>
-          <Helper>Service account credentials for GCP permissions.</Helper>
+          <Helper>AWS access credentials.</Helper>
           <InputRow
             type='text'
             value={this.state.awsAccessId}

+ 23 - 2
dashboard/src/main/home/integrations/integration-form/GCRForm.tsx

@@ -14,18 +14,39 @@ type PropsType = {
 };
 
 type StateType = {
+  credentialsName: string,
+  serviceAccountKey: string,
 };
 
 export default class GCRForm extends Component<PropsType, StateType> {
   state = {
+    credentialsName: '',
+    serviceAccountKey: '',
   }
 
   render() {
     return ( 
       <StyledForm>
         <CredentialWrapper>
-          <Heading>Coming Soon</Heading>
-          <Helper>Under construction.</Helper>
+          <Heading>Porter Settings</Heading>
+          <Helper>Give a name to this set of registry credentials (just for Porter).</Helper>
+          <InputRow
+            type='text'
+            value={this.state.credentialsName}
+            setValue={(x: string) => this.setState({ credentialsName: x })}
+            label='🏷️ Registry Name'
+            placeholder='ex: paper-straw'
+            width='100%'
+          />
+          <Heading>GCP Settings</Heading>
+          <Helper>Service account credentials for GCP permissions.</Helper>
+          <TextArea
+            value={this.state.serviceAccountKey}
+            setValue={(x: string) => this.setState({ serviceAccountKey: x })}
+            label='🔑 Service Account Key (JSON)'
+            placeholder='(Paste your JSON service account key here)'
+            width='100%'
+          />
         </CredentialWrapper>
         <SaveButton
           text='Save Settings'

+ 2 - 2
dashboard/src/main/home/templates/expanded-template/LaunchTemplate.tsx

@@ -29,7 +29,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     currentView: 'repo',
     clusterOptions: [] as { label: string, value: string }[],
     selectedCluster: this.context.currentCluster.name,
-    selectedImageUrl: '',
+    selectedImageUrl: '' as string | null,
     tabOptions: [] as ChoiceType[],
     tabContents: [] as any,
   };
@@ -94,7 +94,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
   }
 
   componentDidUpdate(prevProps: PropsType, prevState: StateType) {
-    if (this.state.selectedImageUrl !== prevState.selectedImageUrl) {
+    if (this.state.selectedImageUrl != prevState.selectedImageUrl) {
       this.refreshTabs();
     }
   }

+ 5 - 0
dashboard/src/shared/types.tsx

@@ -131,4 +131,9 @@ export interface ProjectType {
 export interface ChoiceType {
   value: string,
   label: string
+}
+
+export interface ImageType {
+  kind: string,
+  source: string,
 }