forgeplus-react/src/forge/Main/CoderDepot.jsx

541 lines
22 KiB
React
Raw Normal View History

2021-03-18 17:28:53 +08:00
import React , { useEffect , useState } from 'react';
import { WhiteBack , Box , LongWidth , ShortWidth , Gap , AlignCenter , FlexAJ } from '../Component/layout';
2021-08-12 15:24:45 +08:00
import { Dropdown , Menu , Divider , Spin, Button , Typography } from 'antd';
2021-03-18 17:28:53 +08:00
import { getImageUrl } from "educoder";
import { Link } from 'react-router-dom';
2021-03-19 17:22:02 +08:00
import CloneAddress from '../Branch/CloneAddress';
2021-03-18 17:28:53 +08:00
import SelectBranch from '../Branch/Select';
import User from '../Component/User';
import axios from 'axios';
import Path from './CoderDepotPath';
import Catalogue from './CoderDepotCatalogue';
import ReadMe from './CoderDepotReadme';
import CoderRootFileDetail from './CoderRootFileDetail';
import './Index.scss';
import Releases from '../Component/Releases';
import Contributors from '../Component/Contributors';
import LanguagePower from '../Component/LanguagePower';
import DrawerPanel from '../Component/DrawerPanel';
2021-03-24 14:08:10 +08:00
import UpdateDescModal from './sub/UpdateDescModal';
2021-03-18 17:28:53 +08:00
import Nodata from '../Nodata';
import Invite from './sub/Invite';
/**
* projectDetail.type:0是托管项目1是镜像项目2是同步镜像项目(为2时不支持在线创建在线上传在线修改在线删除创建合并请求等功能)
*/
2021-08-12 15:24:45 +08:00
const { Paragraph } = Typography;
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function returnbar(str){
if(str && str.length>0 && str.indexOf("%2F")>-1){
return str.replaceAll('%2F','/');
}
return str;
}
2021-03-18 17:28:53 +08:00
function CoderDepot(props){
const [ projectDetail , setProjectDetail ]= useState(undefined);
const [ inviteCode , setInviteCode ] = useState(undefined);
2021-03-18 17:28:53 +08:00
const [ treeValue , setTreeValue ] = useState(undefined);
const [ treeValuePath , setTreeValuePath ] = useState(undefined);
const [ lastCommit,setLastCommit ] = useState(undefined);
const [ lastCommitAuthor,setLastCommitAuthor ] = useState(undefined);
const [ type ,setType ] = useState('dir');
const [ hide , setHide ] = useState(true);
const [ hideBtn , setHideBtn ] = useState(false);
const [ commitCount ,setCommitCount ] = useState(0);
const [ dirInfo ,setDirInfo ] = useState(undefined);//文件夹目录列表
const [ fileInfo ,setFileInfo ] = useState(undefined);//文件内容信息
const [ zip_url , setZip_url ] = useState(undefined);
const [ tar_url , setTar_url ] = useState(undefined);
const [ readOnly , setReadOnly] = useState(true);
const [ isSpin , setIsSpin] = useState(true);
const [ visible ,setVisible ] = useState(false);
2021-03-19 16:29:43 +08:00
const [ mainFlag ,setMainFlag ] = useState(false);
2021-03-24 14:08:10 +08:00
const [ openModal , setOpenModal ] = useState(false);
const [ desc , setDesc ] = useState(undefined);
const [ website , setWebsite ] = useState(undefined);
const [ lesson_url , setLessonUrl ] = useState(undefined);
2021-05-26 16:00:55 +08:00
const [ readme , setReadme ] = useState(undefined);
const [ defaultBranch , setDefaultBranch ] = useState(undefined);
const [ editReadme , setEditReadme ] = useState(false);
const [ pullsFlag , setPullsFlag ] = useState(true);
const [ issuesFlag , setIssuesFlag ] = useState(true);
2021-03-18 17:28:53 +08:00
const owner = props.match.params.owner;
const projectsId = props.match.params.projectsId;
let branchName = props.match.params.branchName;
branchName = returnbar(branchName);
2021-05-21 18:58:17 +08:00
const details = props.projectDetail;
2021-03-18 17:28:53 +08:00
let pathname = props.history.location.pathname;
const { bannerList } = props;
useEffect(()=>{
if(bannerList && bannerList.length>0){
let a = bannerList.filter(i=>i.menu_name === "pulls");
let i = bannerList.filter(i=>i.menu_name === "issues");
if(a && a.length === 0){
setPullsFlag(false);
}
if(i && i.length === 0){
setIssuesFlag(false);
}
}
},[bannerList])
2021-05-21 18:58:17 +08:00
2021-03-18 17:28:53 +08:00
useEffect(()=>{
2021-05-21 18:58:17 +08:00
if(details){
setProjectDetail(details);
setDesc(details.description);
setWebsite(details.website);
setLessonUrl(details.lesson_url);
2021-05-26 16:00:55 +08:00
setDefaultBranch(details.default_branch);
setInviteCode(details.invite_code);
2021-03-18 17:28:53 +08:00
}
2021-05-21 18:58:17 +08:00
},[details])
2021-03-18 17:28:53 +08:00
useEffect(()=>{
if(treeValue){
setTreeValuePath(treeValue.split('/'));
}else{
setTreeValuePath(undefined);
}
},[treeValue])
2021-05-21 18:58:17 +08:00
2021-03-18 17:28:53 +08:00
useEffect(()=>{
2021-05-26 16:00:55 +08:00
if (projectsId && owner && defaultBranch){
let b = turnbar(branchName) ;
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${b}/`) > -1) {
let url = pathname.split(`/tree/${b}/`)[1];
2021-03-18 17:28:53 +08:00
setTreeValue(url);
getFileInfo(url,branchName);
setType("file");
2021-03-18 17:28:53 +08:00
}else{
setTreeValue(undefined);
2021-05-26 16:00:55 +08:00
getDirInfo(branchName || defaultBranch);
setType("dir");
2021-03-18 17:28:53 +08:00
}
}
2021-05-26 16:00:55 +08:00
},[projectsId,owner,pathname,defaultBranch])
2021-03-18 17:28:53 +08:00
// 获取主目录列表
function getDirInfo(branch){
setIsSpin(true);
const url = `/${owner}/${projectsId}/entries.json`;
2021-03-18 17:28:53 +08:00
axios.get(url, {
params: { ref: branch }
}).then((result) => {
if (result) {
setCommitCount(result.data.commits_count);
setDirInfo(result.data.entries);
setFileInfo(undefined);
setTar_url(result.data.tar_url);
setZip_url(result.data.zip_url);
let c = result.data.last_commit
setLastCommit(c && c.commit);
2021-03-30 15:01:25 +08:00
setLastCommitAuthor(c && c.committer);
2021-03-19 16:29:43 +08:00
setMainFlag(true);
setReadOnly(true);
2021-05-21 18:58:17 +08:00
setReadme(result.data.readme);
setEditReadme(false);
setHide(true);
2021-03-18 17:28:53 +08:00
}
setTimeout(function(){setIsSpin(false);},500);
}).catch(error=>{setIsSpin(false);})
}
useEffect(()=>{
if(projectDetail && lastCommit)
{
let ele = document.getElementById("ptxt");
if(ele){
let h = ele.offsetHeight;
if( h > 18 ) setHideBtn(true);
2021-03-18 17:28:53 +08:00
}
}
},[projectDetail,lastCommit])
// 获取子目录列表
function getFileInfo(path, ref){
setIsSpin(true);
const url = `/${owner}/${projectsId}/sub_entries.json`;
axios.get(url, {
params:{
filepath:path,
ref:ref || branchName,
type
}
}).then((result) => {
if (result) {
let en = result.data.entries;
if(en.type){
setDirInfo(undefined);
setFileInfo(en);
setType(en.type);
}else{
setFileInfo(undefined);
setDirInfo(en);
setType("dir");
}
let c = result.data.last_commit
setLastCommit(c && c.commit);
2021-03-30 15:01:25 +08:00
setLastCommitAuthor(c && c.committer);
2021-03-19 16:29:43 +08:00
setMainFlag(false);
setReadOnly(!editReadme);
setHide(true);
2021-03-18 17:28:53 +08:00
}
setTimeout(function(){setIsSpin(false);},500)
}).catch(error=>{setIsSpin(false);})
}
// 切换分支或者标签
function changeBranch(value){
let checkvalue = turnbar(value);
let url = `/projects/${owner}/${projectsId}${value && `/tree/${checkvalue}`}${treeValue ? `/${treeValue}`:""}`;
2021-03-18 17:28:53 +08:00
props.history.push(url);
}
// 文件相关的下拉项
function fileMenu(){
let b = branchName || defaultBranch;
let checkvalue = turnbar(b);
return (
2021-03-18 17:28:53 +08:00
<Menu>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${checkvalue}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${checkvalue}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>新建文件</a></Menu.Item>
2021-03-18 17:28:53 +08:00
</Menu>
)
}
2021-03-18 17:28:53 +08:00
function getPathUrl(array,index){
if(array && array.length>0 && index){
let str = "";
for(let i=0;i<index;i++){
str += `/${array[i]}`;
}
return str.substr(1);
}
}
// 页面地址返回到主目录
function returnMain(){
setTreeValue(undefined);
2021-05-26 16:00:55 +08:00
let branch = branchName || defaultBranch;
let checkvalue = turnbar(branch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${checkvalue}`);
2021-03-18 17:28:53 +08:00
};
// 子目录路径返回链接
function returnUlr(url){
let enBranch = turnbar(branchName);
props.history.push(`/projects/${owner}/${projectsId}/tree${enBranch?`/${enBranch}`:""}/${url}`);
2021-03-18 17:28:53 +08:00
}
// 点击跳转到子目录
function goToSubRoot(path,type,filename){
if(type!=="submodule"){
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
setType(type);
props.history.push(`/projects/${owner}/${projectsId}${`/tree/${checkvalue}`}${path?`/${path}`:""}`);
}
2021-03-18 17:28:53 +08:00
}
function onEdit(readOnly){
setReadOnly(readOnly);
setEditReadme(false);
2021-03-18 17:28:53 +08:00
}
function ChangeFile(path, readOnly){
//点击直接跳转页面 加载一次路由
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${checkvalue}/${path}`);
2021-03-18 17:28:53 +08:00
setType("file");
setEditReadme(true);
2021-03-18 17:28:53 +08:00
};
function changeHide(hide){
setHide(!hide);
}
2021-03-19 17:22:02 +08:00
function urlLink(link){
if(props.checkIfLogin()===false){
props.showLoginDialog()
return false;
}
props.history.push(link);
}
2021-03-18 17:28:53 +08:00
const downloadMenu = (
2021-07-22 17:44:12 +08:00
<CloneAddress
http_url={projectDetail && projectDetail.clone_url}
ssh_url = {projectDetail && projectDetail.ssh_url}
zip_url={zip_url}
tar_url={tar_url}
showNotification={props.showNotification}/>
2021-03-18 17:28:53 +08:00
)
2021-04-08 17:44:31 +08:00
// 确认修改简介、website、实践课程链接
function okUpdate(d,w,l){
2021-03-24 14:08:10 +08:00
const url = `/${owner}/${projectsId}.json`;
axios.put(url,{
description:d,website:w,lesson_url:l
2021-03-24 14:08:10 +08:00
}).then(result=>{
if(result && result.data && result.data.id){
setDesc(result.data.description);
setWebsite(result.data.website);
setLessonUrl(result.data.lesson_url);
2021-03-24 14:08:10 +08:00
}
})
}
2021-04-20 15:31:49 +08:00
let n = fileInfo && fileInfo.name;
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
2021-06-11 11:01:46 +08:00
const { current_user } = props;
2021-08-03 17:37:14 +08:00
const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter";
2021-07-06 14:40:23 +08:00
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
2021-03-18 17:28:53 +08:00
return(
<WhiteBack>
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
2021-03-18 17:28:53 +08:00
<Spin spinning={isSpin}>
{
2021-03-19 16:29:43 +08:00
(dirInfo || fileInfo) &&
2021-03-18 17:28:53 +08:00
<React.Fragment>
<DrawerPanel
history={props.history}
owner={owner}
projectsId={projectsId}
name={projectDetail && projectDetail.name}
2021-05-26 16:00:55 +08:00
branch={branchName || defaultBranch}
2021-03-18 17:28:53 +08:00
visible={visible}
onClose={()=>setVisible(false)}
2021-03-19 16:29:43 +08:00
list = {mainFlag ? dirInfo : undefined}
2021-03-18 17:28:53 +08:00
/>
2021-03-30 10:26:02 +08:00
<div className="drawerBtn" onClick={()=>setVisible(true)}>
2021-08-13 17:12:56 +08:00
<i className="iconfont icon-zuohuaicon font-14"></i>
2021-03-30 10:26:02 +08:00
<span>目录</span>
</div>
2021-03-18 17:28:53 +08:00
</React.Fragment>
}
<div style={{minHeight:"500px"}}>
{
projectDetail &&
<Box className="Panels">
<LongWidth>
<div className="panelmenu">
<FlexAJ>
<AlignCenter>
2021-08-13 17:12:56 +08:00
<div className="mr30">
2021-03-18 17:28:53 +08:00
{
props && props.platform ?
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
2021-05-26 16:00:55 +08:00
branch={branchName || defaultBranch}
2021-03-18 17:28:53 +08:00
changeBranch={changeBranch}
owner={owner}
history={props.history}
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
></SelectBranch>
:
2021-05-26 16:00:55 +08:00
<span>分支<span className="color-grey-6">{branchName || defaultBranch}</span></span>
2021-03-18 17:28:53 +08:00
}
</div>
<AlignCenter className="mr20">
2021-08-12 11:45:56 +08:00
<Link to={`/projects/${owner}/${projectsId}/branchs`} className="depotNum">
<i className="iconfont icon-master_icon font-14 mr3"></i>
<span className="mr3">分支</span>
<span>{projectDetail && projectDetail.branches && projectDetail.branches.total_count}</span>
2021-03-18 17:28:53 +08:00
</Link>
</AlignCenter>
<AlignCenter className="mr20">
2021-08-12 11:45:56 +08:00
<Link to={`/projects/${owner}/${projectsId}/tag`} className="depotNum">
<i className="iconfont icon-biaoqianicon font-14 mr3"></i>
<span className="mr3">标签</span>
<span>{projectDetail && projectDetail.tags && projectDetail.tags.total_count}</span>
2021-03-18 17:28:53 +08:00
</Link>
</AlignCenter>
</AlignCenter>
2021-08-13 17:12:56 +08:00
<AlignCenter className="depotBtn">
2021-08-03 17:37:14 +08:00
{
2021-08-12 11:45:56 +08:00
baseOperate && projectDetail.type !== 2 && pullsFlag &&
2021-08-13 17:12:56 +08:00
<Button type="default" onClick={()=>urlLink(`/projects/${owner}/${projectsId}/pulls/new`)} className="mr10"><i className="iconfont icon-xinjian2 font-12 mr3"></i> 合并请求</Button>
2021-08-12 11:45:56 +08:00
}
{
baseOperate && issuesFlag &&
2021-08-13 17:12:56 +08:00
<Button type="default" onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} className="mr10"><i className="iconfont icon-xinjian2 font-12 mr3"></i> 任务</Button>
2021-08-03 17:37:14 +08:00
}
2021-06-11 11:01:46 +08:00
{ fileOperate &&
2021-08-12 11:45:56 +08:00
<Dropdown overlay={fileMenu()} className="mr10" trigger={['click']}>
2021-08-13 17:12:56 +08:00
<Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-6 mr-5"></i></Button>
2021-03-30 11:50:25 +08:00
</Dropdown>
}
2021-04-27 17:11:37 +08:00
<Dropdown overlay={downloadMenu} placement="bottomRight" trigger={['click']}>
2021-08-13 17:12:56 +08:00
<Button type={'primary'}>下载 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-white mr-3"></i></Button>
</Dropdown>
2021-03-18 17:28:53 +08:00
</AlignCenter>
</FlexAJ>
{
dirInfo || fileInfo ?
<div className="listtable">
{
lastCommit &&
<div className="listtablehead">
<User url={getImageUrl(`/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} id={lastCommitAuthor && lastCommitAuthor.id} login={lastCommitAuthor && lastCommitAuthor.login}/>
<div className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}>
<pre id="ptxt">{lastCommit && lastCommit.message}</pre>
</div>
2021-03-18 17:28:53 +08:00
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
2021-08-12 15:24:45 +08:00
2021-08-13 17:12:56 +08:00
<span className="ml20 color-grey-9 mt1">{lastCommit && lastCommit.time_from_now}</span>
2021-08-12 11:45:56 +08:00
{
commitCount ?
2021-08-13 17:12:56 +08:00
<Link to={`/projects/${owner}/${projectsId}/commits/branch/${turnbar(branchName || defaultBranch)}`} className="ml20 color-grey-9" style={{height:"30px",lineHeight:"30px"}}>
<i className="iconfont icon-tijiaoicon mr3 font-16"></i>{commitCount}次提交
2021-08-12 11:45:56 +08:00
</Link>:""
}
2021-03-18 17:28:53 +08:00
</div>
}
<ul className="listtablebody">
{
treeValuePath && treeValuePath.length > 0 &&
<Path
identifier={projectDetail && projectDetail.identifier}
treeValuePath={treeValuePath}
returnUlr={returnUlr}
returnMain={returnMain}
getPathUrl={getPathUrl}
/>
}
{
dirInfo && dirInfo.length > 0 &&
dirInfo.map((item,key)=>{
return(
<Catalogue
owner={owner}
item={item}
projectsId={projectsId}
goToSubRoot={goToSubRoot}
/>
)
})
}
{
fileInfo &&
<CoderRootFileDetail
{...props}
detail={fileInfo}
readOnly={readOnly}
2021-04-20 15:31:49 +08:00
md={mdFlag}
2021-03-18 17:28:53 +08:00
onEdit={onEdit}
2021-05-26 16:00:55 +08:00
currentBranch={branchName || defaultBranch}
type={projectDetail.type}
2021-08-27 16:56:58 +08:00
></CoderRootFileDetail>[[]]
2021-03-18 17:28:53 +08:00
}
</ul>
</div>
2021-03-19 16:29:43 +08:00
: ""
2021-03-18 17:28:53 +08:00
}
2021-03-19 16:29:43 +08:00
{
(dirInfo && dirInfo.length === 0) && (fileInfo && fileInfo.length === 0) ? <Nodata _html="暂未发现文件"/> :""
}
2021-03-18 17:28:53 +08:00
{/* readme文件显示(显示文件详情时不显示readme文件) */}
2021-06-04 10:55:05 +08:00
{ dirInfo && (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
2021-03-18 17:28:53 +08:00
</div>
</LongWidth>
{
!fileInfo &&
<ShortWidth>
<Gap style={{paddingLeft:"30px"}}>
<div className="panelmenu">
2021-08-13 17:12:56 +08:00
<FlexAJ className="font-16 color-ooo mb10" style={{lineHeight:"22px"}}>关于
2021-08-12 15:24:45 +08:00
{projectDetail.permission && (projectDetail.permission==="Admin" || projectDetail.permission==="Owner") &&
<a className="color-grey-6" href="javascript:void(0)"><i onClick={()=>setOpenModal(true)} className="iconfont icon-a-shezhi font-15"></i></a>
}
2021-03-24 14:08:10 +08:00
</FlexAJ>
2021-08-13 17:12:56 +08:00
{desc && <p className="font-14 color-grey-3 mb15 task-hide-2" style={{lineHeight:"24px",WebkitLineClamp:"4",textAlign:"justify",wordBreak:"break-all"}}>{desc}</p>}
2021-03-18 17:28:53 +08:00
{
2021-03-24 14:08:10 +08:00
website &&
2021-08-12 15:24:45 +08:00
<div className="color-grey-6 df pinfos mb5">
<i className="iconfont icon-lianjie2 font-15 mr10"></i>
<a href={website} target="_blank" style={{wordBreak:"break-all",lineHeight:"20px",marginTop:"5px",textDecoration:"underline"}}>{website}</a>
</div>
2021-03-18 17:28:53 +08:00
}
2021-08-12 15:24:45 +08:00
<div className="pinfos mb5">
2021-08-13 17:12:56 +08:00
<i className="iconfont icon-zishuwenjian_icon font-15 mr10"></i>
2021-08-12 15:24:45 +08:00
<a href="#readme">README.md</a>
</div>
<div className="color-grey-6 mb5">
<i className="iconfont icon-neicunicon font-15 mr10"></i>
2021-03-18 17:28:53 +08:00
<span>{projectDetail && projectDetail.size}</span>
2021-08-12 15:24:45 +08:00
</div>
2021-03-18 17:28:53 +08:00
{
projectDetail && projectDetail.license_name &&
2021-08-12 15:24:45 +08:00
<div>
2021-08-13 17:12:56 +08:00
<i className="iconfont icon-xieyiicon font-16 mr10 color-grey-6"></i>
2021-08-12 15:24:45 +08:00
<span className="color-grey-6">{projectDetail.license_name}</span>
</div>
2021-03-18 17:28:53 +08:00
}
</div>
{
inviteCode &&
<div>
<Divider />
2021-08-12 11:45:56 +08:00
<Invite code={inviteCode}/>
</div>
}
{
lesson_url &&
<div>
<Divider />
2021-08-13 17:12:56 +08:00
<p className="font-16 color-ooo">实践课程</p>
2021-08-05 10:57:43 +08:00
<a href={lesson_url} target="_blank" className="color-grey-6" style={{textDecoration:"underline",wordBreak:"break-all"}}>{lesson_url}</a>
</div>
}
2021-03-18 17:28:53 +08:00
{/* 发布 */}
{
projectDetail && projectDetail.release_versions &&
<React.Fragment>
<Divider />
2021-08-27 16:56:58 +08:00
<Releases
2021-08-03 17:37:14 +08:00
owner={owner}
projectsId={projectsId}
releaseVersions={projectDetail.release_versions}
history={props.history}
baseOperate={baseOperate}
projectType={projectDetail.type}
2021-08-03 17:37:14 +08:00
/>
2021-03-18 17:28:53 +08:00
</React.Fragment>
}
{/* 贡献者 */}
{
projectDetail && projectDetail.contributors &&
2021-08-13 17:12:56 +08:00
<Contributors
contributors={projectDetail && projectDetail.contributors}
owner={owner}
projectsId={projectsId}
currentLogin={current_user && current_user.login}
/>
2021-03-18 17:28:53 +08:00
}
{/* 语言 */}
{ projectDetail && projectDetail.languages &&
<React.Fragment>
<Divider />
<LanguagePower languages={projectDetail.languages}/>
</React.Fragment>
}
</Gap>
</ShortWidth>
}
</Box>
}
</div>
</Spin>
</WhiteBack>
)
}
export default CoderDepot;