feat: initial commit

This commit is contained in:
nikola
2026-05-19 14:53:39 +02:00
commit 6d1b20c4b0
90 changed files with 243385 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
__pycache__/
app/__pycache__/
*.pyc
data/
*.png
+9
View File
@@ -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
View File
@@ -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"]
+35
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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)}&#10;TYPE: {_safe(item['file_category'])}&#10;ACCESS: {_safe(access)}&#10;OWNER: {_safe(item['owner_email'])}&#10;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
View File
File diff suppressed because it is too large Load Diff
+146
View File
@@ -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
View File
@@ -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),
}
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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&#10;TYPE: SUBTITLE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: TEXT&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: URL&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: XLSX&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: AUDIO&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_APP&#10;ACCESS: PRIVATE&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_DOC&#10;ACCESS: DOMAIN&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: IMAGE&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: VIDEO&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: ARCHIVE&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: YAML&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: FOLDER&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: G_SHEET&#10;ACCESS: PUBLIC&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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&#10;TYPE: OTHER&#10;ACCESS: SHARED_USERS&#10;OWNER: nikola.jovanovic@wolkabout.com&#10;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
+27
View File
@@ -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
1 root_path file_category access_scope owner_email item_count
2 SharedWithMe IMAGE SHARED_USERS nikola.jovanovic@wolkabout.com 460
3 My Drive SUBTITLE PRIVATE nikola.jovanovic@wolkabout.com 300
4 My Drive VIDEO PRIVATE nikola.jovanovic@wolkabout.com 300
5 SharedWithMe IMAGE PUBLIC nikola.jovanovic@wolkabout.com 276
6 My Drive TEXT PRIVATE nikola.jovanovic@wolkabout.com 37
7 SharedWithMe VIDEO SHARED_USERS nikola.jovanovic@wolkabout.com 30
8 My Drive FOLDER PRIVATE nikola.jovanovic@wolkabout.com 24
9 My Drive URL PRIVATE nikola.jovanovic@wolkabout.com 18
10 SharedWithMe ARCHIVE SHARED_USERS nikola.jovanovic@wolkabout.com 13
11 SharedWithMe YAML SHARED_USERS nikola.jovanovic@wolkabout.com 9
12 My Drive G_SHEET PRIVATE nikola.jovanovic@wolkabout.com 8
13 My Drive IMAGE PRIVATE nikola.jovanovic@wolkabout.com 5
14 My Drive XLSX PRIVATE nikola.jovanovic@wolkabout.com 5
15 SharedWithMe FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 5
16 My Drive G_DOC PRIVATE nikola.jovanovic@wolkabout.com 3
17 My Drive ARCHIVE PRIVATE nikola.jovanovic@wolkabout.com 2
18 My Drive G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 2
19 My Drive AUDIO PUBLIC nikola.jovanovic@wolkabout.com 1
20 My Drive FOLDER SHARED_USERS nikola.jovanovic@wolkabout.com 1
21 My Drive G_APP PRIVATE nikola.jovanovic@wolkabout.com 1
22 My Drive G_DOC DOMAIN nikola.jovanovic@wolkabout.com 1
23 My Drive G_SHEET SHARED_USERS nikola.jovanovic@wolkabout.com 1
24 My Drive PDF SHARED_USERS nikola.jovanovic@wolkabout.com 1
25 SharedWithMe FOLDER PUBLIC nikola.jovanovic@wolkabout.com 1
26 SharedWithMe G_SHEET PUBLIC nikola.jovanovic@wolkabout.com 1
27 SharedWithMe OTHER SHARED_USERS nikola.jovanovic@wolkabout.com 1
File diff suppressed because it is too large Load Diff
+9
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
Flask==3.1.0
Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

+183
View File
@@ -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>
+98
View File
@@ -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()
+61
View File
@@ -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