feat: initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
__pycache__/
|
||||||
|
app/__pycache__/
|
||||||
|
*.pyc
|
||||||
|
data/
|
||||||
|
*.png
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# AGENTS.md - gdrive-visual-app
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
This project builds a local web app for Google Drive CSV ingestion, cleanup, and interactive visualization.
|
||||||
|
|
||||||
|
## Working rules
|
||||||
|
- Keep implementation simple and production-practical.
|
||||||
|
- Prioritize readability of diagram labels and filtering workflow.
|
||||||
|
- Ensure outputs are shareable (`cleaned.csv`, `summary.csv`, diagram view).
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY app ./app
|
||||||
|
COPY templates ./templates
|
||||||
|
COPY static ./static
|
||||||
|
|
||||||
|
RUN find /app/app -type d -name "__pycache__" -prune -exec rm -rf {} + \
|
||||||
|
&& mkdir -p /app/data
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["python", "-m", "app.main"]
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# GDrive Visual Inspector
|
||||||
|
|
||||||
|
Local web app for:
|
||||||
|
- Uploading GAM raw CSV export
|
||||||
|
- Building clean shareable CSV output
|
||||||
|
- Generating interactive diagram (zoom/pan/cards)
|
||||||
|
|
||||||
|
## Run with Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/nikola/codex-cli/projects/gdrive-visual-app
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Open `http://localhost:8080`.
|
||||||
|
|
||||||
|
## Expected CSV input
|
||||||
|
|
||||||
|
Generated via GAM, example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gam user nikola.jovanovic@wolkabout.com print filelist \
|
||||||
|
fullpath showdepth pathdelimiter / \
|
||||||
|
excludetrashed \
|
||||||
|
oneitemperrow showpermissionslast \
|
||||||
|
fields id,name,mimeType,modifiedTime,size,owners.emailAddress,owners.displayName,permissions.type,permissions.role,permissions.emailAddress,permissions.domain,permissions.allowFileDiscovery \
|
||||||
|
> gdrive_test_raw.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
After upload:
|
||||||
|
- `gdrive_curated.csv` (one row per file/folder, human-friendly)
|
||||||
|
- `gdrive_summary.csv` (aggregated for analysis/management)
|
||||||
|
- interactive diagram in the browser
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Package marker for app module.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+213
@@ -0,0 +1,213 @@
|
|||||||
|
from html import escape
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
ACCESS_COLOR = {
|
||||||
|
"PRIVATE": "#6b7280",
|
||||||
|
"SHARED_USERS": "#d97706",
|
||||||
|
"DOMAIN": "#ea580c",
|
||||||
|
"PUBLIC": "#dc2626",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _safe(value: str) -> str:
|
||||||
|
return escape(str(value or ""))
|
||||||
|
|
||||||
|
|
||||||
|
def render_diagram(summary_rows: List[dict], output_html: Path, max_rows: int = 120) -> None:
|
||||||
|
rows = summary_rows[:max_rows]
|
||||||
|
|
||||||
|
by_root: Dict[str, List[dict]] = {}
|
||||||
|
for row in rows:
|
||||||
|
root = row["root_path"]
|
||||||
|
by_root.setdefault(root, []).append(row)
|
||||||
|
|
||||||
|
roots = sorted(by_root.keys())
|
||||||
|
for root in roots:
|
||||||
|
by_root[root].sort(key=lambda x: -int(x["item_count"]))
|
||||||
|
|
||||||
|
total_items = sum(int(r["item_count"]) for r in rows)
|
||||||
|
|
||||||
|
root_cols = []
|
||||||
|
for root in roots:
|
||||||
|
items = by_root[root][:16]
|
||||||
|
root_total = sum(int(x["item_count"]) for x in items)
|
||||||
|
|
||||||
|
cards = []
|
||||||
|
for item in items:
|
||||||
|
access = item["access_scope"]
|
||||||
|
color = ACCESS_COLOR.get(access, "#6b7280")
|
||||||
|
owner = item["owner_email"]
|
||||||
|
if len(owner) > 32:
|
||||||
|
owner = owner[:31] + "..."
|
||||||
|
cards.append(
|
||||||
|
f"""
|
||||||
|
<div class=\"detail-card\" style=\"--card-color:{color}\" title=\"PATH: {_safe(root)} TYPE: {_safe(item['file_category'])} ACCESS: {_safe(access)} OWNER: {_safe(item['owner_email'])} COUNT: {_safe(item['item_count'])}\">
|
||||||
|
<div class=\"line\"><b>TYPE</b> {_safe(item['file_category'])}</div>
|
||||||
|
<div class=\"line\"><b>ACCESS</b> {_safe(access)}</div>
|
||||||
|
<div class=\"line\"><b>OWNER</b> {_safe(owner)}</div>
|
||||||
|
<div class=\"line\"><b>COUNT</b> {_safe(item['item_count'])}</div>
|
||||||
|
</div>
|
||||||
|
""".strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
root_cols.append(
|
||||||
|
f"""
|
||||||
|
<section class=\"root-col\">
|
||||||
|
<div class=\"root-connector\"></div>
|
||||||
|
<div class=\"root-card\">
|
||||||
|
<div class=\"line\"><b>PATH GROUP</b></div>
|
||||||
|
<div class=\"line root-name\">{_safe(root)}</div>
|
||||||
|
<div class=\"line\"><b>ITEMS</b> {_safe(root_total)}</div>
|
||||||
|
</div>
|
||||||
|
<div class=\"cards-stack\">{''.join(cards)}</div>
|
||||||
|
</section>
|
||||||
|
""".strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
html = f"""<!doctype html>
|
||||||
|
<html lang=\"en\">
|
||||||
|
<head>
|
||||||
|
<meta charset=\"utf-8\" />
|
||||||
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {{
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}}
|
||||||
|
* {{ box-sizing: border-box; }}
|
||||||
|
body {{ margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }}
|
||||||
|
.toolbar {{ position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }}
|
||||||
|
.btn {{ border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }}
|
||||||
|
.hint {{ color: #475569; font-size: 12px; margin-left: 8px; }}
|
||||||
|
|
||||||
|
#viewport {{ width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }}
|
||||||
|
#canvas {{ min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }}
|
||||||
|
|
||||||
|
.top-wrap {{ display: flex; justify-content: center; margin-bottom: 36px; position: relative; }}
|
||||||
|
.top-card {{ width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }}
|
||||||
|
.line {{ line-height: 1.22; margin: 2px 0; font-size: 13px; }}
|
||||||
|
|
||||||
|
.roots-row {{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}}
|
||||||
|
|
||||||
|
.roots-row::before {{
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}}
|
||||||
|
|
||||||
|
.root-col {{ position: relative; padding-top: 18px; }}
|
||||||
|
.root-connector {{
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}}
|
||||||
|
|
||||||
|
.root-card {{ width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }}
|
||||||
|
.root-name {{ font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }}
|
||||||
|
|
||||||
|
.cards-stack {{ width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }}
|
||||||
|
.cards-stack::before {{
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}}
|
||||||
|
|
||||||
|
.detail-card {{ width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }}
|
||||||
|
.detail-card::before {{
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {{
|
||||||
|
#viewport {{ height: 70vh; }}
|
||||||
|
#canvas {{ min-width: 980px; }}
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class=\"toolbar\">
|
||||||
|
<button class=\"btn\" onclick=\"zoomIn()\">+</button>
|
||||||
|
<button class=\"btn\" onclick=\"zoomOut()\">-</button>
|
||||||
|
<button class=\"btn\" onclick=\"resetZoom()\">Reset</button>
|
||||||
|
<span class=\"hint\">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id=\"viewport\">
|
||||||
|
<div id=\"canvas\">
|
||||||
|
<div class=\"top-wrap\">
|
||||||
|
<div class=\"top-card\">
|
||||||
|
<div class=\"line\"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class=\"line\"><b>TOTAL ITEMS</b> {_safe(total_items)}</div>
|
||||||
|
<div class=\"line\"><b>GROUPS</b> {_safe(len(roots))}</div>
|
||||||
|
<div class=\"line\"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=\"roots-row\">{''.join(root_cols)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {{
|
||||||
|
canvas.style.transform = `scale(${{scale}})`;
|
||||||
|
}}
|
||||||
|
function zoomIn() {{
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}}
|
||||||
|
function zoomOut() {{
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}}
|
||||||
|
function resetZoom() {{
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({{ top: 0, left: 0, behavior: 'smooth' }});
|
||||||
|
}}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {{
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}}, {{ passive: false }});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
output_html.write_text(html, encoding="utf-8")
|
||||||
+2052
File diff suppressed because it is too large
Load Diff
+146
@@ -0,0 +1,146 @@
|
|||||||
|
import csv
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from flask import Flask, redirect, render_template, request, send_file, url_for
|
||||||
|
|
||||||
|
from app.diagram import render_diagram
|
||||||
|
from app.explorer import render_explorer
|
||||||
|
from app.parser import parse_gam_raw_csv, sharing_matrix, summarize, top_stats
|
||||||
|
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
DATA_DIR = BASE_DIR / "data"
|
||||||
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
app = Flask(
|
||||||
|
__name__,
|
||||||
|
template_folder=str(BASE_DIR / "templates"),
|
||||||
|
static_folder=str(BASE_DIR / "static"),
|
||||||
|
)
|
||||||
|
app.config["MAX_CONTENT_LENGTH"] = 50 * 1024 * 1024
|
||||||
|
|
||||||
|
STATE = {
|
||||||
|
"last_job": None,
|
||||||
|
"last_error": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def write_csv(path: Path, rows: list[dict]) -> None:
|
||||||
|
if not rows:
|
||||||
|
return
|
||||||
|
with path.open("w", newline="", encoding="utf-8") as f:
|
||||||
|
w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
|
||||||
|
w.writeheader()
|
||||||
|
w.writerows(rows)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def index():
|
||||||
|
last_job = STATE["last_job"]
|
||||||
|
context = {
|
||||||
|
"has_data": False,
|
||||||
|
"stats": {},
|
||||||
|
"diagram_path": None,
|
||||||
|
"cleaned_path": None,
|
||||||
|
"summary_path": None,
|
||||||
|
"sharing_matrix_path": None,
|
||||||
|
"explorer_path": None,
|
||||||
|
"explorer_url": None,
|
||||||
|
"build_id": None,
|
||||||
|
"last_upload_time": None,
|
||||||
|
"process_ms": None,
|
||||||
|
"curated_count": None,
|
||||||
|
"error": STATE.get("last_error"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_job:
|
||||||
|
context.update(last_job)
|
||||||
|
context["has_data"] = True
|
||||||
|
|
||||||
|
return render_template("index.html", **context)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/upload")
|
||||||
|
def upload_csv():
|
||||||
|
upload = request.files.get("csv_file")
|
||||||
|
if not upload or not upload.filename:
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
t0 = datetime.now(timezone.utc)
|
||||||
|
job_id = uuid.uuid4().hex[:10]
|
||||||
|
job_dir = DATA_DIR / job_id
|
||||||
|
job_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
raw_path = job_dir / "gdrive_test_raw.csv"
|
||||||
|
upload.save(raw_path)
|
||||||
|
|
||||||
|
curated_rows = parse_gam_raw_csv(raw_path)
|
||||||
|
summary_rows = summarize(curated_rows)
|
||||||
|
sharing_rows = sharing_matrix(curated_rows)
|
||||||
|
stats = top_stats(curated_rows)
|
||||||
|
|
||||||
|
cleaned_csv = job_dir / "gdrive_curated.csv"
|
||||||
|
summary_csv = job_dir / "gdrive_summary.csv"
|
||||||
|
sharing_csv = job_dir / "gdrive_sharing_matrix.csv"
|
||||||
|
diagram_html = job_dir / "diagram.html"
|
||||||
|
explorer_html = job_dir / "explorer.html"
|
||||||
|
|
||||||
|
write_csv(cleaned_csv, curated_rows)
|
||||||
|
write_csv(summary_csv, summary_rows)
|
||||||
|
write_csv(sharing_csv, sharing_rows)
|
||||||
|
render_diagram(summary_rows, diagram_html)
|
||||||
|
build_id = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
|
||||||
|
render_explorer(curated_rows, explorer_html, build_id=build_id)
|
||||||
|
t1 = datetime.now(timezone.utc)
|
||||||
|
process_ms = int((t1 - t0).total_seconds() * 1000)
|
||||||
|
|
||||||
|
STATE["last_job"] = {
|
||||||
|
"stats": stats,
|
||||||
|
"diagram_path": f"/data/{job_id}/diagram.html",
|
||||||
|
"cleaned_path": f"/download/{job_id}/cleaned",
|
||||||
|
"summary_path": f"/download/{job_id}/summary",
|
||||||
|
"sharing_matrix_path": f"/download/{job_id}/sharing-matrix",
|
||||||
|
"explorer_path": f"/data/{job_id}/explorer.html",
|
||||||
|
"explorer_url": f"/data/{job_id}/explorer.html?v={build_id}",
|
||||||
|
"build_id": build_id,
|
||||||
|
"last_upload_time": t1.strftime("%Y-%m-%d %H:%M:%S UTC"),
|
||||||
|
"process_ms": process_ms,
|
||||||
|
"curated_count": len(curated_rows),
|
||||||
|
"job_id": job_id,
|
||||||
|
}
|
||||||
|
STATE["last_error"] = None
|
||||||
|
except Exception as exc:
|
||||||
|
STATE["last_error"] = (
|
||||||
|
"Upload failed. Check CSV format and try again. "
|
||||||
|
f"Error: {exc}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/download/<job_id>/<kind>")
|
||||||
|
def download(job_id: str, kind: str):
|
||||||
|
job_dir = DATA_DIR / job_id
|
||||||
|
if kind == "cleaned":
|
||||||
|
return send_file(job_dir / "gdrive_curated.csv", as_attachment=True)
|
||||||
|
if kind == "summary":
|
||||||
|
return send_file(job_dir / "gdrive_summary.csv", as_attachment=True)
|
||||||
|
if kind == "sharing-matrix":
|
||||||
|
return send_file(job_dir / "gdrive_sharing_matrix.csv", as_attachment=True)
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/data/<job_id>/diagram.html")
|
||||||
|
def serve_diagram(job_id: str):
|
||||||
|
return send_file(DATA_DIR / job_id / "diagram.html")
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/data/<job_id>/explorer.html")
|
||||||
|
def serve_explorer(job_id: str):
|
||||||
|
return send_file(DATA_DIR / job_id / "explorer.html")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=8080, debug=False)
|
||||||
+458
@@ -0,0 +1,458 @@
|
|||||||
|
import csv
|
||||||
|
from collections import Counter, defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
|
FOLDER_MIME = "application/vnd.google-apps.folder"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Item:
|
||||||
|
item_id: str
|
||||||
|
name: str
|
||||||
|
depth: str
|
||||||
|
mime_type: str
|
||||||
|
modified_time: str
|
||||||
|
size_bytes: str
|
||||||
|
owner_email: str
|
||||||
|
owner_name: str
|
||||||
|
path: str
|
||||||
|
sharing_user_email: str
|
||||||
|
last_modifying_user_email: str
|
||||||
|
|
||||||
|
|
||||||
|
def _norm(value: str) -> str:
|
||||||
|
return (value or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _to_bool(value: str) -> bool:
|
||||||
|
return _norm(value).lower() in {"true", "1", "yes"}
|
||||||
|
|
||||||
|
|
||||||
|
def file_category(mime_type: str, name: str) -> str:
|
||||||
|
mime = _norm(mime_type)
|
||||||
|
fname = _norm(name)
|
||||||
|
|
||||||
|
if mime == FOLDER_MIME:
|
||||||
|
return "FOLDER"
|
||||||
|
if mime.startswith("application/vnd.google-apps."):
|
||||||
|
return {
|
||||||
|
"application/vnd.google-apps.spreadsheet": "G_SHEET",
|
||||||
|
"application/vnd.google-apps.document": "G_DOC",
|
||||||
|
"application/vnd.google-apps.presentation": "G_SLIDES",
|
||||||
|
}.get(mime, "G_APP")
|
||||||
|
if mime.startswith("image/"):
|
||||||
|
return "IMAGE"
|
||||||
|
if mime.startswith("video/"):
|
||||||
|
return "VIDEO"
|
||||||
|
if mime.startswith("audio/"):
|
||||||
|
return "AUDIO"
|
||||||
|
if mime in {"application/x-subrip", "text/vtt"}:
|
||||||
|
return "SUBTITLE"
|
||||||
|
if mime in {"application/pdf"}:
|
||||||
|
return "PDF"
|
||||||
|
if mime in {"application/zip", "application/x-7z-compressed"}:
|
||||||
|
return "ARCHIVE"
|
||||||
|
if mime.startswith("text/"):
|
||||||
|
return "TEXT"
|
||||||
|
|
||||||
|
if "." in fname:
|
||||||
|
ext = fname.rsplit(".", 1)[-1].lower()
|
||||||
|
return ext.upper()[:12] if ext else "OTHER"
|
||||||
|
|
||||||
|
return "OTHER"
|
||||||
|
|
||||||
|
|
||||||
|
def _permission_principal(perm: dict) -> str:
|
||||||
|
ptype = perm.get("type", "")
|
||||||
|
email = perm.get("email", "")
|
||||||
|
domain = perm.get("domain", "")
|
||||||
|
|
||||||
|
if ptype == "user":
|
||||||
|
return email or "unknown-user"
|
||||||
|
if ptype == "group":
|
||||||
|
return f"group:{email}" if email else "group:unknown"
|
||||||
|
if ptype == "domain":
|
||||||
|
return f"domain:{domain}" if domain else "domain:unknown"
|
||||||
|
if ptype == "anyone":
|
||||||
|
return "anyone(public)"
|
||||||
|
return email or domain or ptype or "UNKNOWN"
|
||||||
|
|
||||||
|
|
||||||
|
def classify_access(permissions: List[dict]) -> str:
|
||||||
|
active = [p for p in permissions if not p.get("deleted", False)]
|
||||||
|
ptypes = {p["type"] for p in active if p["type"]}
|
||||||
|
owner_only = len(active) == 1 and any(p["role"] == "owner" for p in active)
|
||||||
|
|
||||||
|
if "anyone" in ptypes:
|
||||||
|
return "PUBLIC"
|
||||||
|
if "domain" in ptypes:
|
||||||
|
return "DOMAIN"
|
||||||
|
if owner_only:
|
||||||
|
return "PRIVATE"
|
||||||
|
if "user" in ptypes or "group" in ptypes:
|
||||||
|
return "SHARED_USERS"
|
||||||
|
return "PRIVATE"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_external_email(email: str) -> bool:
|
||||||
|
e = _norm(email).lower()
|
||||||
|
return bool(e and "@" in e and not e.endswith("@wolkabout.com"))
|
||||||
|
|
||||||
|
|
||||||
|
def classify_risk(
|
||||||
|
*,
|
||||||
|
access_scope: str,
|
||||||
|
shared_with_count: int,
|
||||||
|
external_target_count: int,
|
||||||
|
writer_target_count: int,
|
||||||
|
writer_without_expiry_count: int,
|
||||||
|
owner_differs_from_shared_by: bool,
|
||||||
|
) -> tuple[str, str, int, str]:
|
||||||
|
score = 0
|
||||||
|
flags: List[str] = []
|
||||||
|
reasons: List[str] = []
|
||||||
|
|
||||||
|
if access_scope == "PUBLIC":
|
||||||
|
score += 75
|
||||||
|
flags.append("PUBLIC_LINK")
|
||||||
|
reasons.append("Javno dostupno (anyone link)")
|
||||||
|
elif access_scope == "DOMAIN":
|
||||||
|
score += 60
|
||||||
|
flags.append("DOMAIN_WIDE")
|
||||||
|
reasons.append("Dostupno celom domenu")
|
||||||
|
|
||||||
|
if shared_with_count >= 20:
|
||||||
|
score += 45
|
||||||
|
flags.append("MASS_SHARING_20")
|
||||||
|
reasons.append(f"Masovno deljenje ({shared_with_count} primalaca)")
|
||||||
|
elif shared_with_count >= 10:
|
||||||
|
score += 30
|
||||||
|
flags.append("MASS_SHARING_10")
|
||||||
|
reasons.append(f"Siroko deljenje ({shared_with_count} primalaca)")
|
||||||
|
elif shared_with_count >= 5:
|
||||||
|
score += 18
|
||||||
|
flags.append("MULTI_SHARING_5")
|
||||||
|
reasons.append(f"Vise primalaca ({shared_with_count})")
|
||||||
|
|
||||||
|
if external_target_count >= 5:
|
||||||
|
score += 35
|
||||||
|
flags.append("EXTERNAL_5")
|
||||||
|
reasons.append(f"Deljeno van firme ({external_target_count} eksternih)")
|
||||||
|
elif external_target_count >= 1:
|
||||||
|
score += 20
|
||||||
|
flags.append("EXTERNAL_1")
|
||||||
|
reasons.append("Deljeno van firme")
|
||||||
|
|
||||||
|
if writer_target_count >= 3:
|
||||||
|
score += 22
|
||||||
|
flags.append("WRITER_3")
|
||||||
|
reasons.append(f"Vise edit prava ({writer_target_count})")
|
||||||
|
elif writer_target_count >= 1:
|
||||||
|
score += 12
|
||||||
|
flags.append("WRITER_1")
|
||||||
|
reasons.append("Postoje edit prava")
|
||||||
|
|
||||||
|
if writer_without_expiry_count >= 1:
|
||||||
|
score += 10
|
||||||
|
flags.append("WRITER_NO_EXPIRY")
|
||||||
|
reasons.append("Edit prava bez isteka")
|
||||||
|
|
||||||
|
if owner_differs_from_shared_by:
|
||||||
|
score += 8
|
||||||
|
flags.append("RESHARED")
|
||||||
|
reasons.append("Deljenje nije pokrenuo originalni owner")
|
||||||
|
|
||||||
|
if access_scope == "PRIVATE" and shared_with_count == 0:
|
||||||
|
score = max(0, score - 8)
|
||||||
|
|
||||||
|
score = max(0, min(score, 100))
|
||||||
|
if score >= 70:
|
||||||
|
level = "HIGH"
|
||||||
|
elif score >= 35:
|
||||||
|
level = "MEDIUM"
|
||||||
|
else:
|
||||||
|
level = "LOW"
|
||||||
|
|
||||||
|
reason = "; ".join(reasons[:3]) if reasons else "Nema sirokog deljenja"
|
||||||
|
return level, reason, score, "|".join(flags)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_gam_raw_csv(raw_csv_path: Path) -> List[dict]:
|
||||||
|
by_id: Dict[str, Item] = {}
|
||||||
|
permissions_by_id: Dict[str, List[dict]] = defaultdict(list)
|
||||||
|
|
||||||
|
with raw_csv_path.open(newline="", encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
item_id = _norm(row.get("id"))
|
||||||
|
if not item_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if item_id not in by_id:
|
||||||
|
sharing_user = _norm(row.get("sharingUser.emailAddress"))
|
||||||
|
if not sharing_user:
|
||||||
|
sharing_user = _norm(row.get("sharingUser.0.emailAddress"))
|
||||||
|
|
||||||
|
last_mod = _norm(row.get("lastModifyingUser.emailAddress"))
|
||||||
|
if not last_mod:
|
||||||
|
last_mod = _norm(row.get("lastModifyingUser.0.emailAddress"))
|
||||||
|
|
||||||
|
by_id[item_id] = Item(
|
||||||
|
item_id=item_id,
|
||||||
|
name=_norm(row.get("name")),
|
||||||
|
depth=_norm(row.get("depth")),
|
||||||
|
mime_type=_norm(row.get("mimeType")),
|
||||||
|
modified_time=_norm(row.get("modifiedTime")),
|
||||||
|
size_bytes=_norm(row.get("size")),
|
||||||
|
owner_email=_norm(row.get("owners.0.emailAddress")),
|
||||||
|
owner_name=_norm(row.get("owners.0.displayName")),
|
||||||
|
path=_norm(row.get("path.0")),
|
||||||
|
sharing_user_email=sharing_user,
|
||||||
|
last_modifying_user_email=last_mod,
|
||||||
|
)
|
||||||
|
|
||||||
|
permissions_by_id[item_id].append(
|
||||||
|
{
|
||||||
|
"type": _norm(row.get("permission.type")),
|
||||||
|
"role": _norm(row.get("permission.role")),
|
||||||
|
"email": _norm(row.get("permission.emailAddress")),
|
||||||
|
"domain": _norm(row.get("permission.domain")),
|
||||||
|
"allow_discovery": _to_bool(row.get("permission.allowFileDiscovery", "")),
|
||||||
|
"id": _norm(row.get("permission.id")),
|
||||||
|
"deleted": _to_bool(row.get("permission.deleted", "")),
|
||||||
|
"expiration_time": _norm(row.get("permission.expirationTime")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
curated_rows: List[dict] = []
|
||||||
|
for item_id, base in by_id.items():
|
||||||
|
raw_permissions = permissions_by_id[item_id]
|
||||||
|
|
||||||
|
unique = {}
|
||||||
|
for p in raw_permissions:
|
||||||
|
key = (
|
||||||
|
p.get("type", ""),
|
||||||
|
p.get("role", ""),
|
||||||
|
p.get("email", ""),
|
||||||
|
p.get("domain", ""),
|
||||||
|
p.get("allow_discovery", False),
|
||||||
|
p.get("expiration_time", ""),
|
||||||
|
p.get("deleted", False),
|
||||||
|
)
|
||||||
|
unique[key] = p
|
||||||
|
permissions = list(unique.values())
|
||||||
|
|
||||||
|
access_scope = classify_access(permissions)
|
||||||
|
path = base.path
|
||||||
|
|
||||||
|
root_path = path.split("/", 1)[0] if path else "UNKNOWN"
|
||||||
|
parent_path = path.rsplit("/", 1)[0] if "/" in path else root_path
|
||||||
|
|
||||||
|
active_non_owner = [
|
||||||
|
p
|
||||||
|
for p in permissions
|
||||||
|
if not p["deleted"] and p["role"] != "owner" and (p["type"] or p["email"] or p["domain"])
|
||||||
|
]
|
||||||
|
|
||||||
|
shared_targets = sorted({_permission_principal(p) for p in active_non_owner})
|
||||||
|
external_target_count = len(
|
||||||
|
[
|
||||||
|
p
|
||||||
|
for p in active_non_owner
|
||||||
|
if _is_external_email(p.get("email", ""))
|
||||||
|
or (
|
||||||
|
p.get("type") == "domain"
|
||||||
|
and p.get("domain")
|
||||||
|
and _norm(p.get("domain", "")).lower() != "wolkabout.com"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
writer_target_count = len(
|
||||||
|
[
|
||||||
|
p
|
||||||
|
for p in active_non_owner
|
||||||
|
if p.get("role", "").lower() in {"writer", "organizer", "fileorganizer"}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
writer_without_expiry_count = len(
|
||||||
|
[
|
||||||
|
p
|
||||||
|
for p in active_non_owner
|
||||||
|
if p.get("role", "").lower() in {"writer", "organizer", "fileorganizer"}
|
||||||
|
and not p.get("expiration_time")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
permission_entries = sorted(
|
||||||
|
{
|
||||||
|
f"{p['type']}:{_permission_principal(p)}:{p['role']}"
|
||||||
|
for p in permissions
|
||||||
|
if not p["deleted"] and p["type"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
direct_people = sorted(
|
||||||
|
{
|
||||||
|
p["email"]
|
||||||
|
for p in active_non_owner
|
||||||
|
if p["type"] in {"user", "group"} and p["email"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
direct_domains = sorted(
|
||||||
|
{
|
||||||
|
p["domain"]
|
||||||
|
for p in active_non_owner
|
||||||
|
if p["type"] == "domain" and p["domain"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Best effort for "who shared":
|
||||||
|
# 1) sharingUser (if GAM provides it), 2) last modifying user, 3) owner.
|
||||||
|
shared_by = (
|
||||||
|
base.sharing_user_email
|
||||||
|
or base.last_modifying_user_email
|
||||||
|
or base.owner_email
|
||||||
|
or "UNKNOWN"
|
||||||
|
)
|
||||||
|
shared_with_count = len(shared_targets)
|
||||||
|
risk_level, risk_reason, risk_score, risk_flags = classify_risk(
|
||||||
|
access_scope=access_scope,
|
||||||
|
shared_with_count=shared_with_count,
|
||||||
|
external_target_count=external_target_count,
|
||||||
|
writer_target_count=writer_target_count,
|
||||||
|
writer_without_expiry_count=writer_without_expiry_count,
|
||||||
|
owner_differs_from_shared_by=bool(
|
||||||
|
base.owner_email
|
||||||
|
and shared_by
|
||||||
|
and base.owner_email.lower() != shared_by.lower()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
curated_rows.append(
|
||||||
|
{
|
||||||
|
"item_id": base.item_id,
|
||||||
|
"name": base.name,
|
||||||
|
"item_kind": "FOLDER" if base.mime_type == FOLDER_MIME else "FILE",
|
||||||
|
"path": path,
|
||||||
|
"parent_path": parent_path,
|
||||||
|
"root_path": root_path,
|
||||||
|
"depth": base.depth,
|
||||||
|
"owner_email": base.owner_email,
|
||||||
|
"owner_name": base.owner_name,
|
||||||
|
"original_owner_email": base.owner_email,
|
||||||
|
"shared_by_email": shared_by,
|
||||||
|
"last_modifying_user_email": base.last_modifying_user_email,
|
||||||
|
"access_scope": access_scope,
|
||||||
|
"access_targets": "|".join(shared_targets),
|
||||||
|
"direct_user_group_targets": "|".join(direct_people),
|
||||||
|
"direct_domain_targets": "|".join(direct_domains),
|
||||||
|
"permission_entries": "|".join(permission_entries),
|
||||||
|
"shared_with_count": shared_with_count,
|
||||||
|
"risk_level": risk_level,
|
||||||
|
"risk_reason": risk_reason,
|
||||||
|
"risk_score": risk_score,
|
||||||
|
"risk_flags": risk_flags,
|
||||||
|
"is_shared": str(access_scope != "PRIVATE"),
|
||||||
|
"mime_type": base.mime_type,
|
||||||
|
"file_category": file_category(base.mime_type, base.name),
|
||||||
|
"size_bytes": base.size_bytes,
|
||||||
|
"modified_time": base.modified_time,
|
||||||
|
"external_target_count": external_target_count,
|
||||||
|
"writer_target_count": writer_target_count,
|
||||||
|
"permission_count": str(len([p for p in permissions if not p['deleted']])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
curated_rows.sort(key=lambda x: (x["root_path"], x["path"], x["name"]))
|
||||||
|
return curated_rows
|
||||||
|
|
||||||
|
|
||||||
|
def summarize(curated_rows: List[dict]) -> List[dict]:
|
||||||
|
agg: Dict[Tuple[str, str, str, str], int] = defaultdict(int)
|
||||||
|
for row in curated_rows:
|
||||||
|
key = (
|
||||||
|
row["root_path"],
|
||||||
|
row["file_category"],
|
||||||
|
row["access_scope"],
|
||||||
|
row["owner_email"],
|
||||||
|
)
|
||||||
|
agg[key] += 1
|
||||||
|
|
||||||
|
output = []
|
||||||
|
for (root_path, file_category_, access_scope, owner_email), item_count in agg.items():
|
||||||
|
output.append(
|
||||||
|
{
|
||||||
|
"root_path": root_path,
|
||||||
|
"file_category": file_category_,
|
||||||
|
"access_scope": access_scope,
|
||||||
|
"owner_email": owner_email,
|
||||||
|
"item_count": item_count,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
output.sort(key=lambda x: (-x["item_count"], x["root_path"], x["file_category"]))
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def sharing_matrix(curated_rows: List[dict]) -> List[dict]:
|
||||||
|
out: List[dict] = []
|
||||||
|
|
||||||
|
for row in curated_rows:
|
||||||
|
entries = [e for e in row.get("permission_entries", "").split("|") if e]
|
||||||
|
|
||||||
|
if not entries:
|
||||||
|
out.append(
|
||||||
|
{
|
||||||
|
"item_id": row["item_id"],
|
||||||
|
"name": row["name"],
|
||||||
|
"path": row["path"],
|
||||||
|
"original_owner_email": row["original_owner_email"],
|
||||||
|
"shared_by_email": row["shared_by_email"],
|
||||||
|
"shared_to": "owner-only",
|
||||||
|
"permission_type": "user",
|
||||||
|
"permission_role": "owner",
|
||||||
|
"access_scope": row["access_scope"],
|
||||||
|
"file_category": row["file_category"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for ent in entries:
|
||||||
|
# format: type:principal:role
|
||||||
|
parts = ent.split(":", 2)
|
||||||
|
if len(parts) != 3:
|
||||||
|
continue
|
||||||
|
ptype, principal, role = parts
|
||||||
|
|
||||||
|
out.append(
|
||||||
|
{
|
||||||
|
"item_id": row["item_id"],
|
||||||
|
"name": row["name"],
|
||||||
|
"path": row["path"],
|
||||||
|
"original_owner_email": row["original_owner_email"],
|
||||||
|
"shared_by_email": row["shared_by_email"],
|
||||||
|
"shared_to": principal,
|
||||||
|
"permission_type": ptype,
|
||||||
|
"permission_role": role,
|
||||||
|
"access_scope": row["access_scope"],
|
||||||
|
"file_category": row["file_category"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
out.sort(key=lambda x: (x["path"], x["shared_to"], x["permission_role"]))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def top_stats(curated_rows: List[dict]) -> dict:
|
||||||
|
return {
|
||||||
|
"total_items": len(curated_rows),
|
||||||
|
"total_folders": sum(1 for x in curated_rows if x["item_kind"] == "FOLDER"),
|
||||||
|
"public_items": sum(1 for x in curated_rows if x["access_scope"] == "PUBLIC"),
|
||||||
|
"domain_items": sum(1 for x in curated_rows if x["access_scope"] == "DOMAIN"),
|
||||||
|
"shared_user_items": sum(
|
||||||
|
1 for x in curated_rows if x["access_scope"] == "SHARED_USERS"
|
||||||
|
),
|
||||||
|
"roots": Counter(x["root_path"] for x in curated_rows).most_common(8),
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>GDrive Diagram</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef2f6;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--card-text: #ffffff;
|
||||||
|
--top: #111827;
|
||||||
|
--path: #facc15;
|
||||||
|
--path-text: #1f2937;
|
||||||
|
--line: #e11d48;
|
||||||
|
--soft-line: #cbd5e1;
|
||||||
|
--card-w: 270px;
|
||||||
|
--card-h: 116px;
|
||||||
|
--radius: 10px;
|
||||||
|
--font: "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); }
|
||||||
|
.toolbar { position: sticky; top: 0; z-index: 5; background: #ffffffeb; backdrop-filter: blur(6px); border-bottom: 1px solid #dbe2ea; padding: 10px 12px; display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn { border: 1px solid #cdd7e2; background: #fff; color: #111827; border-radius: 8px; height: 34px; min-width: 34px; font-weight: 700; cursor: pointer; }
|
||||||
|
.hint { color: #475569; font-size: 12px; margin-left: 8px; }
|
||||||
|
|
||||||
|
#viewport { width: 100%; height: 810px; overflow: auto; background: linear-gradient(145deg,#edf2f7,#f8fafc); }
|
||||||
|
#canvas { min-width: 1400px; min-height: 1000px; transform-origin: top left; padding: 36px 24px 40px; }
|
||||||
|
|
||||||
|
.top-wrap { display: flex; justify-content: center; margin-bottom: 36px; position: relative; }
|
||||||
|
.top-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); background: var(--top); color: var(--card-text); border: 1px solid #0b1220; padding: 10px 12px; font-size: 13px; box-shadow: 0 5px 18px rgba(17,24,39,.18); }
|
||||||
|
.line { line-height: 1.22; margin: 2px 0; font-size: 13px; }
|
||||||
|
|
||||||
|
.roots-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(320px,1fr));
|
||||||
|
gap: 32px;
|
||||||
|
align-items: start;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roots-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 5%;
|
||||||
|
right: 5%;
|
||||||
|
border-top: 3px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-col { position: relative; padding-top: 18px; }
|
||||||
|
.root-connector {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
height: 18px;
|
||||||
|
border-left: 3px solid var(--line);
|
||||||
|
transform: translateX(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-card { width: var(--card-w); height: var(--card-h); margin: 0 auto 14px; border-radius: var(--radius); background: var(--path); color: var(--path-text); border: 1px solid #eab308; padding: 10px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(234,179,8,.24); }
|
||||||
|
.root-name { font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
|
||||||
|
.cards-stack { width: var(--card-w); margin: 0 auto; position: relative; padding-left: 14px; }
|
||||||
|
.cards-stack::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 0;
|
||||||
|
border-left: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card { width: var(--card-w); height: var(--card-h); border-radius: var(--radius); margin: 10px 0; background: var(--card-color); color: var(--card-text); border: 1px solid rgba(15,23,42,.32); padding: 10px 12px; box-shadow: 0 4px 14px rgba(2,6,23,.14); position: relative; font-size: 13px; }
|
||||||
|
.detail-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -14px;
|
||||||
|
top: calc(50% - 1px);
|
||||||
|
width: 14px;
|
||||||
|
border-top: 2px solid var(--soft-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
#viewport { height: 70vh; }
|
||||||
|
#canvas { min-width: 980px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="btn" onclick="zoomIn()">+</button>
|
||||||
|
<button class="btn" onclick="zoomOut()">-</button>
|
||||||
|
<button class="btn" onclick="resetZoom()">Reset</button>
|
||||||
|
<span class="hint">Zoom/pan: scroll to move, Ctrl+scroll to zoom</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="viewport">
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="top-wrap">
|
||||||
|
<div class="top-card">
|
||||||
|
<div class="line"><b>GOOGLE DRIVE INVENTORY</b></div>
|
||||||
|
<div class="line"><b>TOTAL ITEMS</b> 1506</div>
|
||||||
|
<div class="line"><b>GROUPS</b> 2</div>
|
||||||
|
<div class="line"><b>VIEW MODE</b> Org-chart cards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roots-row"><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">My Drive</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 709</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: SUBTITLE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> SUBTITLE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: VIDEO ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 300">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 300</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: TEXT ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 37">
|
||||||
|
<div class="line"><b>TYPE</b> TEXT</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 37</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: FOLDER ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 24">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 24</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: URL ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 18">
|
||||||
|
<div class="line"><b>TYPE</b> URL</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 18</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_SHEET ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 8">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 8</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: IMAGE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: XLSX ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> XLSX</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_DOC ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 3">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 3</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: ARCHIVE ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 2">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 2</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: My Drive TYPE: AUDIO ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> AUDIO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#6b7280" title="PATH: My Drive TYPE: G_APP ACCESS: PRIVATE OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_APP</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PRIVATE</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#ea580c" title="PATH: My Drive TYPE: G_DOC ACCESS: DOMAIN OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_DOC</div>
|
||||||
|
<div class="line"><b>ACCESS</b> DOMAIN</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: My Drive TYPE: G_SHEET ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section><section class="root-col">
|
||||||
|
<div class="root-connector"></div>
|
||||||
|
<div class="root-card">
|
||||||
|
<div class="line"><b>PATH GROUP</b></div>
|
||||||
|
<div class="line root-name">SharedWithMe</div>
|
||||||
|
<div class="line"><b>ITEMS</b> 796</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards-stack"><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 460">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 460</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: IMAGE ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 276">
|
||||||
|
<div class="line"><b>TYPE</b> IMAGE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 276</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: VIDEO ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 30">
|
||||||
|
<div class="line"><b>TYPE</b> VIDEO</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 30</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: ARCHIVE ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 13">
|
||||||
|
<div class="line"><b>TYPE</b> ARCHIVE</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 13</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: YAML ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 9">
|
||||||
|
<div class="line"><b>TYPE</b> YAML</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 9</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 5">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 5</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: FOLDER ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> FOLDER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#dc2626" title="PATH: SharedWithMe TYPE: G_SHEET ACCESS: PUBLIC OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> G_SHEET</div>
|
||||||
|
<div class="line"><b>ACCESS</b> PUBLIC</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div><div class="detail-card" style="--card-color:#d97706" title="PATH: SharedWithMe TYPE: OTHER ACCESS: SHARED_USERS OWNER: nikola.jovanovic@wolkabout.com COUNT: 1">
|
||||||
|
<div class="line"><b>TYPE</b> OTHER</div>
|
||||||
|
<div class="line"><b>ACCESS</b> SHARED_USERS</div>
|
||||||
|
<div class="line"><b>OWNER</b> nikola.jovanovic@wolkabout.com</div>
|
||||||
|
<div class="line"><b>COUNT</b> 1</div>
|
||||||
|
</div></div>
|
||||||
|
</section></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let scale = 1;
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const viewport = document.getElementById('viewport');
|
||||||
|
|
||||||
|
function applyScale() {
|
||||||
|
canvas.style.transform = `scale(${scale})`;
|
||||||
|
}
|
||||||
|
function zoomIn() {
|
||||||
|
scale = Math.min(2.4, +(scale + 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
scale = Math.max(0.45, +(scale - 0.1).toFixed(2));
|
||||||
|
applyScale();
|
||||||
|
}
|
||||||
|
function resetZoom() {
|
||||||
|
scale = 1;
|
||||||
|
applyScale();
|
||||||
|
viewport.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.addEventListener('wheel', (e) => {
|
||||||
|
if (!e.ctrlKey) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY < 0) zoomIn(); else zoomOut();
|
||||||
|
}, { passive: false });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
root_path,file_category,access_scope,owner_email,item_count
|
||||||
|
SharedWithMe,IMAGE,SHARED_USERS,nikola.jovanovic@wolkabout.com,460
|
||||||
|
My Drive,SUBTITLE,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
My Drive,VIDEO,PRIVATE,nikola.jovanovic@wolkabout.com,300
|
||||||
|
SharedWithMe,IMAGE,PUBLIC,nikola.jovanovic@wolkabout.com,276
|
||||||
|
My Drive,TEXT,PRIVATE,nikola.jovanovic@wolkabout.com,37
|
||||||
|
SharedWithMe,VIDEO,SHARED_USERS,nikola.jovanovic@wolkabout.com,30
|
||||||
|
My Drive,FOLDER,PRIVATE,nikola.jovanovic@wolkabout.com,24
|
||||||
|
My Drive,URL,PRIVATE,nikola.jovanovic@wolkabout.com,18
|
||||||
|
SharedWithMe,ARCHIVE,SHARED_USERS,nikola.jovanovic@wolkabout.com,13
|
||||||
|
SharedWithMe,YAML,SHARED_USERS,nikola.jovanovic@wolkabout.com,9
|
||||||
|
My Drive,G_SHEET,PRIVATE,nikola.jovanovic@wolkabout.com,8
|
||||||
|
My Drive,IMAGE,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,XLSX,PRIVATE,nikola.jovanovic@wolkabout.com,5
|
||||||
|
SharedWithMe,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,5
|
||||||
|
My Drive,G_DOC,PRIVATE,nikola.jovanovic@wolkabout.com,3
|
||||||
|
My Drive,ARCHIVE,PRIVATE,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,2
|
||||||
|
My Drive,AUDIO,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,FOLDER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_APP,PRIVATE,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_DOC,DOMAIN,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,G_SHEET,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
My Drive,PDF,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,FOLDER,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,G_SHEET,PUBLIC,nikola.jovanovic@wolkabout.com,1
|
||||||
|
SharedWithMe,OTHER,SHARED_USERS,nikola.jovanovic@wolkabout.com,1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
gdrive-visual-inspector:
|
||||||
|
build: .
|
||||||
|
container_name: gdrive-visual-inspector
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
restart: unless-stopped
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
Flask==3.1.0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 434 KiB |
@@ -0,0 +1,183 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>Wolkabout GDrive Visual Inspector</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #ebf1f5;
|
||||||
|
--panel: #ffffff;
|
||||||
|
--ink: #0f172a;
|
||||||
|
--muted: #4b5563;
|
||||||
|
--line: #d5dde6;
|
||||||
|
--accent: #0f766e;
|
||||||
|
--accent-2: #1d4ed8;
|
||||||
|
--danger: #b91c1c;
|
||||||
|
--shadow: 0 8px 24px rgba(15,23,42,.08);
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Segoe UI", Arial, sans-serif;
|
||||||
|
color: var(--ink);
|
||||||
|
background:
|
||||||
|
radial-gradient(1200px 500px at 5% -20%, #dce8ef 0%, transparent 60%),
|
||||||
|
radial-gradient(1100px 500px at 105% 0%, #d9efe9 0%, transparent 58%),
|
||||||
|
var(--bg);
|
||||||
|
}
|
||||||
|
.wrap { width: 100%; max-width: none; margin: 0; padding: 14px; }
|
||||||
|
.panel {
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.4fr 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.title { font-size: 1.65rem; font-weight: 800; margin: 0 0 5px; }
|
||||||
|
.subtitle { color: var(--muted); margin: 0; }
|
||||||
|
.upload-form { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 10px; }
|
||||||
|
input[type="file"] { border: 1px dashed #bcc8d6; background: #f8fafc; border-radius: 9px; padding: 10px; min-width: 280px; }
|
||||||
|
button, .btn {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 9px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
button { background: var(--accent); color: #fff; }
|
||||||
|
.btn.secondary { background: var(--accent-2); color: #fff; }
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit,minmax(170px,1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.stat { border: 1px solid var(--line); border-radius: 10px; padding: 10px; background: #f8fbfe; }
|
||||||
|
.k { color: var(--muted); font-size: .76rem; text-transform: uppercase; letter-spacing: .04em; }
|
||||||
|
.v { font-size: 1.23rem; font-weight: 800; margin-top: 3px; }
|
||||||
|
|
||||||
|
.error {
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
background: #fef2f2;
|
||||||
|
color: var(--danger);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagram-wrap { margin-top: 12px; }
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 860px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.small { color: var(--muted); font-size: .9rem; margin-top: 6px; }
|
||||||
|
.info-panel {
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #f8fbfe;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.info-panel .row {
|
||||||
|
font-size: .9rem;
|
||||||
|
margin: 5px 0;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
.status-title { font-weight: 800; margin-bottom: 8px; }
|
||||||
|
.status-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2,minmax(0,1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.status-box {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #f8fbfe;
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: .85rem;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
.status-box b { display:block; color:#334155; margin-bottom: 2px; font-size: .76rem; text-transform: uppercase; letter-spacing: .03em; }
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.header { grid-template-columns: 1fr; }
|
||||||
|
iframe { height: 72vh; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="header">
|
||||||
|
<section class="panel">
|
||||||
|
<h1 class="title">Wolkabout GDrive Visual Inspector</h1>
|
||||||
|
<p class="subtitle">Upload GAM CSV -> auto-parse -> interactive org-chart diagram + CSV exports</p>
|
||||||
|
|
||||||
|
<form class="upload-form" action="/upload" method="post" enctype="multipart/form-data">
|
||||||
|
<input type="file" name="csv_file" accept=".csv" required />
|
||||||
|
<button type="submit">Upload And Generate</button>
|
||||||
|
{% if has_data %}
|
||||||
|
<a class="btn secondary" href="{{ cleaned_path }}">Download Cleaned CSV</a>
|
||||||
|
<a class="btn secondary" href="{{ summary_path }}">Download Summary CSV</a>
|
||||||
|
<a class="btn secondary" href="{{ sharing_matrix_path }}">Download Sharing Matrix CSV</a>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="small">Use tree + sunburst for structure, and share network for access flow.</div>
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
<div class="error">{{ error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_data %}
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat"><div class="k">Total items</div><div class="v">{{ stats.total_items }}</div></div>
|
||||||
|
<div class="stat"><div class="k">Folders</div><div class="v">{{ stats.total_folders }}</div></div>
|
||||||
|
<div class="stat"><div class="k">Public</div><div class="v">{{ stats.public_items }}</div></div>
|
||||||
|
<div class="stat"><div class="k">Domain</div><div class="v">{{ stats.domain_items }}</div></div>
|
||||||
|
<div class="stat"><div class="k">Shared users</div><div class="v">{{ stats.shared_user_items }}</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="small">Tip: click a folder/file to sync tree, sunburst and network on the same item.</div>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<div class="status-title">Live Status</div>
|
||||||
|
<div class="status-grid">
|
||||||
|
<div class="status-box"><b>Build ID</b>{{ build_id or "-" }}</div>
|
||||||
|
<div class="status-box"><b>Rows Parsed</b>{{ curated_count or "-" }}</div>
|
||||||
|
<div class="status-box"><b>Last Upload</b>{{ last_upload_time or "-" }}</div>
|
||||||
|
<div class="status-box"><b>Processing Time</b>{{ process_ms or "-" }} ms</div>
|
||||||
|
</div>
|
||||||
|
<div class="small" style="margin-top:10px;">This panel is informational and updates after each CSV upload.</div>
|
||||||
|
{% if build_id %}
|
||||||
|
<div class="small" style="margin-top:8px;"><b>Status ID:</b> {{ build_id }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if has_data %}
|
||||||
|
<div class="diagram-wrap">
|
||||||
|
<iframe src="{{ explorer_url or explorer_path }}"></iframe>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,98 @@
|
|||||||
|
import io
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
try:
|
||||||
|
from app.main import STATE, app
|
||||||
|
except ModuleNotFoundError as exc: # pragma: no cover
|
||||||
|
STATE = {}
|
||||||
|
app = None
|
||||||
|
_IMPORT_ERR = exc
|
||||||
|
else:
|
||||||
|
_IMPORT_ERR = None
|
||||||
|
|
||||||
|
|
||||||
|
def _sample_csv_bytes() -> bytes:
|
||||||
|
header = ",".join(
|
||||||
|
[
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"depth",
|
||||||
|
"mimeType",
|
||||||
|
"modifiedTime",
|
||||||
|
"size",
|
||||||
|
"owners.0.emailAddress",
|
||||||
|
"owners.0.displayName",
|
||||||
|
"path.0",
|
||||||
|
"sharingUser.emailAddress",
|
||||||
|
"lastModifyingUser.emailAddress",
|
||||||
|
"permission.type",
|
||||||
|
"permission.role",
|
||||||
|
"permission.emailAddress",
|
||||||
|
"permission.domain",
|
||||||
|
"permission.allowFileDiscovery",
|
||||||
|
"permission.id",
|
||||||
|
"permission.deleted",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
row = ",".join(
|
||||||
|
[
|
||||||
|
"file-1",
|
||||||
|
"doc.txt",
|
||||||
|
"2",
|
||||||
|
"text/plain",
|
||||||
|
"2026-02-19T10:00:00Z",
|
||||||
|
"128",
|
||||||
|
"owner@example.com",
|
||||||
|
"Owner",
|
||||||
|
"My Drive/docs/doc.txt",
|
||||||
|
"owner@example.com",
|
||||||
|
"owner@example.com",
|
||||||
|
"user",
|
||||||
|
"owner",
|
||||||
|
"owner@example.com",
|
||||||
|
"",
|
||||||
|
"false",
|
||||||
|
"perm-1",
|
||||||
|
"false",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return f"{header}\n{row}\n".encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
class UploadFlowE2ETest(unittest.TestCase):
|
||||||
|
@unittest.skipIf(app is None, f"Flask runtime unavailable: {_IMPORT_ERR}")
|
||||||
|
def test_upload_renders_versioned_explorer_and_operational_panel(self) -> None:
|
||||||
|
client = app.test_client()
|
||||||
|
res = client.post(
|
||||||
|
"/upload",
|
||||||
|
data={"csv_file": (io.BytesIO(_sample_csv_bytes()), "sample.csv")},
|
||||||
|
content_type="multipart/form-data",
|
||||||
|
follow_redirects=False,
|
||||||
|
)
|
||||||
|
self.assertEqual(res.status_code, 302)
|
||||||
|
self.assertEqual(res.headers.get("Location"), "/")
|
||||||
|
|
||||||
|
index_html = client.get("/").get_data(as_text=True)
|
||||||
|
self.assertIn("Build ID:", index_html)
|
||||||
|
self.assertIn("Operational:", index_html)
|
||||||
|
self.assertIn("Processing time:", index_html)
|
||||||
|
self.assertIn("explorer.html?v=", index_html)
|
||||||
|
|
||||||
|
job = STATE.get("last_job") or {}
|
||||||
|
self.assertTrue(job.get("job_id"))
|
||||||
|
self.assertTrue(job.get("build_id"))
|
||||||
|
|
||||||
|
explorer = client.get(f"/data/{job['job_id']}/explorer.html").get_data(as_text=True)
|
||||||
|
self.assertIn("id=\"splitter1\"", explorer)
|
||||||
|
self.assertIn("id=\"splitter2\"", explorer)
|
||||||
|
self.assertIn("copy-row-path", explorer)
|
||||||
|
self.assertIn("Copy Visible Paths", explorer)
|
||||||
|
self.assertIn("const MATRIX_BATCH", explorer)
|
||||||
|
self.assertIn("Global Security Score", explorer)
|
||||||
|
self.assertIn("data-risk=\"external\"", explorer)
|
||||||
|
self.assertIn("let selectedItemId = \"\";", explorer)
|
||||||
|
self.assertIn("function masterFocusRows()", explorer)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from app.explorer import render_explorer
|
||||||
|
|
||||||
|
|
||||||
|
class SunburstContractTest(unittest.TestCase):
|
||||||
|
def test_label_engine_contract_in_html(self) -> None:
|
||||||
|
rows = [
|
||||||
|
{
|
||||||
|
"item_id": "1",
|
||||||
|
"name": "A very very very long folder name for overlap testing",
|
||||||
|
"path": "My Drive/Team buildings & Team events/Very Long Folder Name Here",
|
||||||
|
"root_path": "My Drive",
|
||||||
|
"item_kind": "FOLDER",
|
||||||
|
"file_category": "FOLDER",
|
||||||
|
"original_owner_email": "owner@example.com",
|
||||||
|
"shared_by_email": "owner@example.com",
|
||||||
|
"access_scope": "SHARED_USERS",
|
||||||
|
"shared_with_count": 3,
|
||||||
|
"access_targets": "a@example.com|b@example.com|c@example.com",
|
||||||
|
"permission_entries": "",
|
||||||
|
"risk_level": "LOW",
|
||||||
|
"risk_reason": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "2",
|
||||||
|
"name": "another-really-long-file-name-to-force-truncation.yaml",
|
||||||
|
"path": "My Drive/Team buildings & Team events/Very Long Folder Name Here/another-really-long-file-name-to-force-truncation.yaml",
|
||||||
|
"root_path": "My Drive",
|
||||||
|
"item_kind": "FILE",
|
||||||
|
"file_category": "YAML",
|
||||||
|
"original_owner_email": "owner@example.com",
|
||||||
|
"shared_by_email": "owner@example.com",
|
||||||
|
"access_scope": "PRIVATE",
|
||||||
|
"shared_with_count": 0,
|
||||||
|
"access_targets": "",
|
||||||
|
"permission_entries": "",
|
||||||
|
"risk_level": "LOW",
|
||||||
|
"risk_reason": "",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as td:
|
||||||
|
out = Path(td) / "explorer.html"
|
||||||
|
render_explorer(rows, out, build_id="TESTBUILD")
|
||||||
|
html = out.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
self.assertIn("const SUN_LABEL_MIN_ANGLE", html)
|
||||||
|
self.assertIn("const SUN_LABEL_MIN_RING_PX", html)
|
||||||
|
self.assertIn("function applySunLabelRules", html)
|
||||||
|
self.assertIn("labelText", html)
|
||||||
|
self.assertIn("rotate: 0", html)
|
||||||
|
self.assertIn("formatter: (p) => (p?.data?.labelText || '')", html)
|
||||||
|
self.assertIn("hideOverlap: true", html)
|
||||||
|
self.assertIn("Build: ${BUILD_ID || '-'}", html)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 388 KiB |
Reference in New Issue
Block a user